From cedd34759a918c1464561d21c5a51588dcc74f29 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 7 May 2024 12:40:57 +0200 Subject: [PATCH] AMI test client for VoLTE development --- ami_test_client/.gitignore | 1 + ami_test_client/LICENSE | 339 +++ ami_test_client/Makefile | 20 + ami_test_client/NOTES.txt | 69 + ami_test_client/README.md | 76 + ami_test_client/ami.c | 864 +++++++ ami_test_client/ami.h | 135 + ami_test_client/ami_test_client.c | 279 ++ ami_test_client/libc-jss/API_CHANGES.txt | 30 + ami_test_client/libc-jss/Doxyfile | 1510 +++++++++++ ami_test_client/libc-jss/LICENSE | 339 +++ ami_test_client/libc-jss/Makefile | 29 + ami_test_client/libc-jss/NOTES.txt | 15 + ami_test_client/libc-jss/README.md | 28 + ami_test_client/libc-jss/amysql/Makefile | 19 + ami_test_client/libc-jss/amysql/NOTES.txt | 30 + ami_test_client/libc-jss/amysql/amysql.c | 321 +++ ami_test_client/libc-jss/amysql/amysql.h | 55 + ami_test_client/libc-jss/amysql/test_amysql.c | 57 + .../libc-jss/amysql/test_sync_amysql.c | 40 + ami_test_client/libc-jss/debug.h | 12 + ami_test_client/libc-jss/fmtsub_test.c | 62 + ami_test_client/libc-jss/htclient/Makefile | 11 + ami_test_client/libc-jss/htclient/htclient.c | 235 ++ ami_test_client/libc-jss/htclient/htclient.h | 67 + .../libc-jss/htclient/test_htclient.c | 66 + .../libc-jss/htclient/url_parser.c | 329 +++ .../libc-jss/htclient/url_parser.h | 49 + ami_test_client/libc-jss/ini/LICENSE.txt | 27 + ami_test_client/libc-jss/ini/Makefile | 9 + ami_test_client/libc-jss/ini/README.txt | 5 + .../libc-jss/ini/cpp/INIReader.cpp | 76 + ami_test_client/libc-jss/ini/cpp/INIReader.h | 53 + .../libc-jss/ini/cpp/INIReaderTest.cpp | 21 + .../libc-jss/ini/examples/config.def | 8 + .../libc-jss/ini/examples/ini_dump.c | 40 + .../libc-jss/ini/examples/ini_example.c | 44 + .../libc-jss/ini/examples/ini_xmacros.c | 46 + .../libc-jss/ini/examples/test.ini | 10 + .../libc-jss/ini/extra/Makefile.static | 19 + ami_test_client/libc-jss/ini/ini.c | 176 ++ ami_test_client/libc-jss/ini/ini.h | 72 + ami_test_client/libc-jss/ini/test.ini | 6 + ami_test_client/libc-jss/ini/test_ini.c | 45 + .../libc-jss/ini/tests/bad_comment.ini | 1 + .../libc-jss/ini/tests/bad_multi.ini | 1 + .../libc-jss/ini/tests/bad_section.ini | 5 + .../libc-jss/ini/tests/baseline_multi.txt | 47 + .../libc-jss/ini/tests/baseline_single.txt | 43 + ami_test_client/libc-jss/ini/tests/bom.ini | 3 + .../libc-jss/ini/tests/multi_line.ini | 15 + ami_test_client/libc-jss/ini/tests/normal.ini | 25 + .../libc-jss/ini/tests/unittest.bat | 2 + ami_test_client/libc-jss/ini/tests/unittest.c | 58 + .../libc-jss/ini/tests/user_error.ini | 4 + ami_test_client/libc-jss/logger.c | 165 ++ ami_test_client/libc-jss/logger.h | 45 + ami_test_client/libc-jss/logger_test.c | 43 + ami_test_client/libc-jss/misc.c | 285 +++ ami_test_client/libc-jss/misc.h | 58 + ami_test_client/libc-jss/netsocket.c | 524 ++++ ami_test_client/libc-jss/netsocket.h | 78 + ami_test_client/libc-jss/pipe.c | 200 ++ ami_test_client/libc-jss/test.c | 181 ++ ami_test_client/libc-jss/tracer/Makefile | 11 + ami_test_client/libc-jss/tracer/test_tracer.c | 33 + ami_test_client/libc-jss/tracer/tracer.c | 185 ++ ami_test_client/libc-jss/tracer/tracer.h | 110 + ami_test_client/libc-jss/udns/COPYING.LGPL | 502 ++++ ami_test_client/libc-jss/udns/Makefile.in | 197 ++ ami_test_client/libc-jss/udns/NEWS | 136 + ami_test_client/libc-jss/udns/NOTES | 226 ++ ami_test_client/libc-jss/udns/TODO | 59 + ami_test_client/libc-jss/udns/configure.lib | 268 ++ ami_test_client/libc-jss/udns/dnsget.1 | 195 ++ ami_test_client/libc-jss/udns/dnsget.c | 759 ++++++ ami_test_client/libc-jss/udns/ex-rdns.c | 114 + ami_test_client/libc-jss/udns/getopt.c | 165 ++ ami_test_client/libc-jss/udns/inet_XtoX.c | 327 +++ ami_test_client/libc-jss/udns/rblcheck.1 | 151 ++ ami_test_client/libc-jss/udns/rblcheck.c | 378 +++ ami_test_client/libc-jss/udns/udns.3 | 1352 ++++++++++ ami_test_client/libc-jss/udns/udns.h | 778 ++++++ ami_test_client/libc-jss/udns/udns_XtoX.c | 50 + ami_test_client/libc-jss/udns/udns_bl.c | 160 ++ ami_test_client/libc-jss/udns/udns_codes.c | 199 ++ ami_test_client/libc-jss/udns/udns_dn.c | 379 +++ ami_test_client/libc-jss/udns/udns_dntosp.c | 30 + ami_test_client/libc-jss/udns/udns_init.c | 231 ++ ami_test_client/libc-jss/udns/udns_jran.c | 52 + ami_test_client/libc-jss/udns/udns_misc.c | 67 + ami_test_client/libc-jss/udns/udns_parse.c | 169 ++ ami_test_client/libc-jss/udns/udns_resolver.c | 1323 ++++++++++ ami_test_client/libc-jss/udns/udns_rr_a.c | 123 + ami_test_client/libc-jss/udns/udns_rr_mx.c | 91 + ami_test_client/libc-jss/udns/udns_rr_naptr.c | 128 + ami_test_client/libc-jss/udns/udns_rr_ptr.c | 109 + ami_test_client/libc-jss/udns/udns_rr_srv.c | 155 ++ ami_test_client/libc-jss/udns/udns_rr_txt.c | 98 + ami_test_client/libc-jss/utarray.h | 233 ++ ami_test_client/libc-jss/uthash-1.9.6/LICENSE | 21 + ami_test_client/libc-jss/uthash-1.9.6/README | 41 + .../libc-jss/uthash-1.9.6/doc/Makefile | 43 + .../libc-jss/uthash-1.9.6/doc/NOTES | 44 + .../uthash-1.9.6/doc/html/ChangeLog.html | 1347 ++++++++++ .../uthash-1.9.6/doc/html/img/banner.png | Bin 0 -> 20477 bytes .../uthash-1.9.6/doc/html/img/banner.svg | 451 ++++ .../uthash-1.9.6/doc/html/img/grad_blue.png | Bin 0 -> 290 bytes .../uthash-1.9.6/doc/html/img/grad_blue.svg | 103 + .../uthash-1.9.6/doc/html/img/rss.png | Bin 0 -> 689 bytes .../uthash-1.9.6/doc/html/img/uthash-mini.png | Bin 0 -> 3611 bytes .../uthash-1.9.6/doc/html/img/uthash-mini.svg | 288 +++ .../uthash-1.9.6/doc/html/img/uthash.png | Bin 0 -> 21518 bytes .../libc-jss/uthash-1.9.6/doc/html/index.html | 134 + .../uthash-1.9.6/doc/html/license.html | 59 + .../libc-jss/uthash-1.9.6/doc/html/styles.css | 130 + .../uthash-1.9.6/doc/html/tdh-quirks.css | 41 + .../libc-jss/uthash-1.9.6/doc/html/tdh.css | 402 +++ .../libc-jss/uthash-1.9.6/doc/html/toc.css | 35 + .../uthash-1.9.6/doc/html/userguide.html | 2240 +++++++++++++++++ .../uthash-1.9.6/doc/html/userguide.pdf | 1 + .../uthash-1.9.6/doc/html/utarray.html | 596 +++++ .../uthash-1.9.6/doc/html/utlist.html | 465 ++++ .../uthash-1.9.6/doc/html/utstring.html | 353 +++ .../uthash-1.9.6/doc/pdf/userguide.pdf | Bin 0 -> 315233 bytes .../uthash-1.9.6/doc/txt/ChangeLog.txt | 202 ++ .../libc-jss/uthash-1.9.6/doc/txt/sflogo.txt | 5 + .../libc-jss/uthash-1.9.6/doc/txt/toc.txt | 85 + .../libc-jss/uthash-1.9.6/doc/txt/topnav.txt | 10 + .../uthash-1.9.6/doc/txt/topnav_utarray.txt | 8 + .../uthash-1.9.6/doc/txt/topnav_utlist.txt | 8 + .../uthash-1.9.6/doc/txt/topnav_utstring.txt | 8 + .../uthash-1.9.6/doc/txt/userguide.txt | 1682 +++++++++++++ .../libc-jss/uthash-1.9.6/doc/txt/utarray.txt | 376 +++ .../libc-jss/uthash-1.9.6/doc/txt/utlist.txt | 219 ++ .../uthash-1.9.6/doc/txt/utstring.txt | 178 ++ .../libc-jss/uthash-1.9.6/src/utarray.h | 233 ++ .../libc-jss/uthash-1.9.6/src/uthash.h | 915 +++++++ .../libc-jss/uthash-1.9.6/src/utlist.h | 522 ++++ .../libc-jss/uthash-1.9.6/src/utstring.h | 148 ++ .../libc-jss/uthash-1.9.6/tests/Makefile | 106 + .../libc-jss/uthash-1.9.6/tests/README | 98 + .../libc-jss/uthash-1.9.6/tests/all_funcs | 14 + .../libc-jss/uthash-1.9.6/tests/bloom_perf.c | 64 + .../libc-jss/uthash-1.9.6/tests/bloom_perf.sh | 17 + .../libc-jss/uthash-1.9.6/tests/do_tests | 21 + .../uthash-1.9.6/tests/do_tests.cygwin | 22 + .../uthash-1.9.6/tests/do_tests.mingw | 20 + .../uthash-1.9.6/tests/do_tests_win32.cmd | 16 + .../libc-jss/uthash-1.9.6/tests/emit_keys.c | 44 + .../libc-jss/uthash-1.9.6/tests/example.c | 125 + .../libc-jss/uthash-1.9.6/tests/hashscan.c | 588 +++++ .../libc-jss/uthash-1.9.6/tests/keystat.c | 219 ++ .../libc-jss/uthash-1.9.6/tests/keystats | 40 + .../libc-jss/uthash-1.9.6/tests/mexpand | 21 + .../libc-jss/uthash-1.9.6/tests/simkeys.pl | 28 + .../libc-jss/uthash-1.9.6/tests/sleep_test.c | 29 + .../libc-jss/uthash-1.9.6/tests/tdiff.cpp | 34 + .../libc-jss/uthash-1.9.6/tests/test1.ans | 10 + .../libc-jss/uthash-1.9.6/tests/test1.c | 27 + .../libc-jss/uthash-1.9.6/tests/test10.ans | 4 + .../libc-jss/uthash-1.9.6/tests/test10.c | 45 + .../libc-jss/uthash-1.9.6/tests/test11.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test11.c | 50 + .../libc-jss/uthash-1.9.6/tests/test11.dat | 51 + .../libc-jss/uthash-1.9.6/tests/test12.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test12.c | 34 + .../libc-jss/uthash-1.9.6/tests/test13.ans | 5 + .../libc-jss/uthash-1.9.6/tests/test13.c | 42 + .../libc-jss/uthash-1.9.6/tests/test14.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test14.c | 46 + .../libc-jss/uthash-1.9.6/tests/test14.dat | 1219 +++++++++ .../libc-jss/uthash-1.9.6/tests/test15.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test15.c | 34 + .../libc-jss/uthash-1.9.6/tests/test16.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test16.c | 46 + .../libc-jss/uthash-1.9.6/tests/test17.ans | 134 + .../libc-jss/uthash-1.9.6/tests/test17.c | 55 + .../libc-jss/uthash-1.9.6/tests/test18.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test18.c | 33 + .../libc-jss/uthash-1.9.6/tests/test19.ans | 1012 ++++++++ .../libc-jss/uthash-1.9.6/tests/test19.c | 52 + .../libc-jss/uthash-1.9.6/tests/test2.ans | 5 + .../libc-jss/uthash-1.9.6/tests/test2.c | 31 + .../libc-jss/uthash-1.9.6/tests/test20.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test20.c | 28 + .../libc-jss/uthash-1.9.6/tests/test21.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test21.c | 38 + .../libc-jss/uthash-1.9.6/tests/test22.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test22.c | 59 + .../libc-jss/uthash-1.9.6/tests/test23.ans | 6 + .../libc-jss/uthash-1.9.6/tests/test23.c | 44 + .../libc-jss/uthash-1.9.6/tests/test24.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test24.c | 25 + .../libc-jss/uthash-1.9.6/tests/test25.ans | 28 + .../libc-jss/uthash-1.9.6/tests/test25.c | 112 + .../libc-jss/uthash-1.9.6/tests/test26.ans | 53 + .../libc-jss/uthash-1.9.6/tests/test26.c | 52 + .../libc-jss/uthash-1.9.6/tests/test27.ans | 28 + .../libc-jss/uthash-1.9.6/tests/test27.c | 112 + .../libc-jss/uthash-1.9.6/tests/test28.ans | 34 + .../libc-jss/uthash-1.9.6/tests/test28.c | 132 + .../libc-jss/uthash-1.9.6/tests/test29.ans | 103 + .../libc-jss/uthash-1.9.6/tests/test29.c | 48 + .../libc-jss/uthash-1.9.6/tests/test3.ans | 5 + .../libc-jss/uthash-1.9.6/tests/test3.c | 38 + .../libc-jss/uthash-1.9.6/tests/test30.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test30.c | 43 + .../libc-jss/uthash-1.9.6/tests/test31.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test31.c | 43 + .../libc-jss/uthash-1.9.6/tests/test32.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test32.c | 41 + .../libc-jss/uthash-1.9.6/tests/test33.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test33.c | 43 + .../libc-jss/uthash-1.9.6/tests/test34.ans | 51 + .../libc-jss/uthash-1.9.6/tests/test34.c | 41 + .../libc-jss/uthash-1.9.6/tests/test35.ans | 30 + .../libc-jss/uthash-1.9.6/tests/test35.c | 37 + .../libc-jss/uthash-1.9.6/tests/test36.ans | 15 + .../libc-jss/uthash-1.9.6/tests/test36.c | 46 + .../libc-jss/uthash-1.9.6/tests/test37.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test37.c | 51 + .../libc-jss/uthash-1.9.6/tests/test38.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test38.c | 28 + .../libc-jss/uthash-1.9.6/tests/test39.ans | 7 + .../libc-jss/uthash-1.9.6/tests/test39.c | 31 + .../libc-jss/uthash-1.9.6/tests/test4.ans | 10 + .../libc-jss/uthash-1.9.6/tests/test4.c | 29 + .../libc-jss/uthash-1.9.6/tests/test40.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test40.c | 34 + .../libc-jss/uthash-1.9.6/tests/test41.ans | 6 + .../libc-jss/uthash-1.9.6/tests/test41.c | 58 + .../libc-jss/uthash-1.9.6/tests/test42.ans | 14 + .../libc-jss/uthash-1.9.6/tests/test42.c | 65 + .../libc-jss/uthash-1.9.6/tests/test43.ans | 88 + .../libc-jss/uthash-1.9.6/tests/test43.c | 76 + .../libc-jss/uthash-1.9.6/tests/test44.ans | 9 + .../libc-jss/uthash-1.9.6/tests/test44.c | 44 + .../libc-jss/uthash-1.9.6/tests/test45.ans | 3 + .../libc-jss/uthash-1.9.6/tests/test45.c | 24 + .../libc-jss/uthash-1.9.6/tests/test46.ans | 11 + .../libc-jss/uthash-1.9.6/tests/test46.c | 49 + .../libc-jss/uthash-1.9.6/tests/test47.ans | 8 + .../libc-jss/uthash-1.9.6/tests/test47.c | 28 + .../libc-jss/uthash-1.9.6/tests/test48.ans | 10 + .../libc-jss/uthash-1.9.6/tests/test48.c | 20 + .../libc-jss/uthash-1.9.6/tests/test49.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test49.c | 20 + .../libc-jss/uthash-1.9.6/tests/test5.ans | 5 + .../libc-jss/uthash-1.9.6/tests/test5.c | 33 + .../libc-jss/uthash-1.9.6/tests/test50.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test50.c | 19 + .../libc-jss/uthash-1.9.6/tests/test51.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test51.c | 28 + .../libc-jss/uthash-1.9.6/tests/test52.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test52.c | 39 + .../libc-jss/uthash-1.9.6/tests/test53.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test53.c | 13 + .../libc-jss/uthash-1.9.6/tests/test54.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test54.c | 23 + .../libc-jss/uthash-1.9.6/tests/test55.ans | 2 + .../libc-jss/uthash-1.9.6/tests/test55.c | 18 + .../libc-jss/uthash-1.9.6/tests/test56.ans | 65 + .../libc-jss/uthash-1.9.6/tests/test56.c | 86 + .../libc-jss/uthash-1.9.6/tests/test57.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test57.c | 28 + .../libc-jss/uthash-1.9.6/tests/test58.ans | 18 + .../libc-jss/uthash-1.9.6/tests/test58.c | 55 + .../libc-jss/uthash-1.9.6/tests/test59.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test59.c | 51 + .../libc-jss/uthash-1.9.6/tests/test6.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test6.c | 52 + .../libc-jss/uthash-1.9.6/tests/test60.ans | 1 + .../libc-jss/uthash-1.9.6/tests/test60.c | 45 + .../libc-jss/uthash-1.9.6/tests/test61.ans | 16 + .../libc-jss/uthash-1.9.6/tests/test61.c | 42 + .../libc-jss/uthash-1.9.6/tests/test62.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test62.c | 61 + .../libc-jss/uthash-1.9.6/tests/test63.ans | 7 + .../libc-jss/uthash-1.9.6/tests/test63.c | 47 + .../libc-jss/uthash-1.9.6/tests/test64.ans | 7 + .../libc-jss/uthash-1.9.6/tests/test64.c | 47 + .../libc-jss/uthash-1.9.6/tests/test65.ans | 4 + .../libc-jss/uthash-1.9.6/tests/test65.c | 72 + .../libc-jss/uthash-1.9.6/tests/test65.dat | 51 + .../libc-jss/uthash-1.9.6/tests/test66.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test66.c | 37 + .../libc-jss/uthash-1.9.6/tests/test67.ans | 20 + .../libc-jss/uthash-1.9.6/tests/test67.c | 27 + .../libc-jss/uthash-1.9.6/tests/test7.ans | 0 .../libc-jss/uthash-1.9.6/tests/test7.c | 38 + .../libc-jss/uthash-1.9.6/tests/test8.ans | 15 + .../libc-jss/uthash-1.9.6/tests/test8.c | 34 + .../libc-jss/uthash-1.9.6/tests/test9.ans | 500 ++++ .../libc-jss/uthash-1.9.6/tests/test9.c | 31 + .../uthash-1.9.6/tests/threads/Makefile | 31 + .../uthash-1.9.6/tests/threads/README | 2 + .../uthash-1.9.6/tests/threads/do_tests | 22 + .../uthash-1.9.6/tests/threads/test1.c | 111 + .../uthash-1.9.6/tests/threads/test2.c | 70 + ami_test_client/libc-jss/uthash.h | 915 +++++++ ami_test_client/libc-jss/utlist.h | 522 ++++ ami_test_client/libc-jss/utstring.h | 148 ++ ami_test_client/milenage.c | 429 ++++ ami_test_client/milenage.h | 22 + ami_test_client/originate.c | 337 +++ ami_test_client/originate.h | 49 + ami_test_client/volcon_config.h | 4 + 308 files changed, 39639 insertions(+) create mode 100644 ami_test_client/.gitignore create mode 100644 ami_test_client/LICENSE create mode 100644 ami_test_client/Makefile create mode 100644 ami_test_client/NOTES.txt create mode 100644 ami_test_client/README.md create mode 100644 ami_test_client/ami.c create mode 100644 ami_test_client/ami.h create mode 100644 ami_test_client/ami_test_client.c create mode 100644 ami_test_client/libc-jss/API_CHANGES.txt create mode 100644 ami_test_client/libc-jss/Doxyfile create mode 100644 ami_test_client/libc-jss/LICENSE create mode 100644 ami_test_client/libc-jss/Makefile create mode 100644 ami_test_client/libc-jss/NOTES.txt create mode 100644 ami_test_client/libc-jss/README.md create mode 100644 ami_test_client/libc-jss/amysql/Makefile create mode 100644 ami_test_client/libc-jss/amysql/NOTES.txt create mode 100644 ami_test_client/libc-jss/amysql/amysql.c create mode 100644 ami_test_client/libc-jss/amysql/amysql.h create mode 100644 ami_test_client/libc-jss/amysql/test_amysql.c create mode 100644 ami_test_client/libc-jss/amysql/test_sync_amysql.c create mode 100644 ami_test_client/libc-jss/debug.h create mode 100644 ami_test_client/libc-jss/fmtsub_test.c create mode 100644 ami_test_client/libc-jss/htclient/Makefile create mode 100644 ami_test_client/libc-jss/htclient/htclient.c create mode 100644 ami_test_client/libc-jss/htclient/htclient.h create mode 100644 ami_test_client/libc-jss/htclient/test_htclient.c create mode 100644 ami_test_client/libc-jss/htclient/url_parser.c create mode 100644 ami_test_client/libc-jss/htclient/url_parser.h create mode 100644 ami_test_client/libc-jss/ini/LICENSE.txt create mode 100644 ami_test_client/libc-jss/ini/Makefile create mode 100644 ami_test_client/libc-jss/ini/README.txt create mode 100644 ami_test_client/libc-jss/ini/cpp/INIReader.cpp create mode 100644 ami_test_client/libc-jss/ini/cpp/INIReader.h create mode 100644 ami_test_client/libc-jss/ini/cpp/INIReaderTest.cpp create mode 100644 ami_test_client/libc-jss/ini/examples/config.def create mode 100644 ami_test_client/libc-jss/ini/examples/ini_dump.c create mode 100644 ami_test_client/libc-jss/ini/examples/ini_example.c create mode 100644 ami_test_client/libc-jss/ini/examples/ini_xmacros.c create mode 100644 ami_test_client/libc-jss/ini/examples/test.ini create mode 100644 ami_test_client/libc-jss/ini/extra/Makefile.static create mode 100644 ami_test_client/libc-jss/ini/ini.c create mode 100644 ami_test_client/libc-jss/ini/ini.h create mode 100644 ami_test_client/libc-jss/ini/test.ini create mode 100644 ami_test_client/libc-jss/ini/test_ini.c create mode 100644 ami_test_client/libc-jss/ini/tests/bad_comment.ini create mode 100644 ami_test_client/libc-jss/ini/tests/bad_multi.ini create mode 100644 ami_test_client/libc-jss/ini/tests/bad_section.ini create mode 100644 ami_test_client/libc-jss/ini/tests/baseline_multi.txt create mode 100644 ami_test_client/libc-jss/ini/tests/baseline_single.txt create mode 100644 ami_test_client/libc-jss/ini/tests/bom.ini create mode 100644 ami_test_client/libc-jss/ini/tests/multi_line.ini create mode 100644 ami_test_client/libc-jss/ini/tests/normal.ini create mode 100644 ami_test_client/libc-jss/ini/tests/unittest.bat create mode 100644 ami_test_client/libc-jss/ini/tests/unittest.c create mode 100644 ami_test_client/libc-jss/ini/tests/user_error.ini create mode 100644 ami_test_client/libc-jss/logger.c create mode 100644 ami_test_client/libc-jss/logger.h create mode 100644 ami_test_client/libc-jss/logger_test.c create mode 100644 ami_test_client/libc-jss/misc.c create mode 100644 ami_test_client/libc-jss/misc.h create mode 100644 ami_test_client/libc-jss/netsocket.c create mode 100644 ami_test_client/libc-jss/netsocket.h create mode 100644 ami_test_client/libc-jss/pipe.c create mode 100644 ami_test_client/libc-jss/test.c create mode 100644 ami_test_client/libc-jss/tracer/Makefile create mode 100644 ami_test_client/libc-jss/tracer/test_tracer.c create mode 100644 ami_test_client/libc-jss/tracer/tracer.c create mode 100644 ami_test_client/libc-jss/tracer/tracer.h create mode 100644 ami_test_client/libc-jss/udns/COPYING.LGPL create mode 100644 ami_test_client/libc-jss/udns/Makefile.in create mode 100644 ami_test_client/libc-jss/udns/NEWS create mode 100644 ami_test_client/libc-jss/udns/NOTES create mode 100644 ami_test_client/libc-jss/udns/TODO create mode 100644 ami_test_client/libc-jss/udns/configure.lib create mode 100644 ami_test_client/libc-jss/udns/dnsget.1 create mode 100644 ami_test_client/libc-jss/udns/dnsget.c create mode 100644 ami_test_client/libc-jss/udns/ex-rdns.c create mode 100644 ami_test_client/libc-jss/udns/getopt.c create mode 100644 ami_test_client/libc-jss/udns/inet_XtoX.c create mode 100644 ami_test_client/libc-jss/udns/rblcheck.1 create mode 100644 ami_test_client/libc-jss/udns/rblcheck.c create mode 100644 ami_test_client/libc-jss/udns/udns.3 create mode 100644 ami_test_client/libc-jss/udns/udns.h create mode 100644 ami_test_client/libc-jss/udns/udns_XtoX.c create mode 100644 ami_test_client/libc-jss/udns/udns_bl.c create mode 100644 ami_test_client/libc-jss/udns/udns_codes.c create mode 100644 ami_test_client/libc-jss/udns/udns_dn.c create mode 100644 ami_test_client/libc-jss/udns/udns_dntosp.c create mode 100644 ami_test_client/libc-jss/udns/udns_init.c create mode 100644 ami_test_client/libc-jss/udns/udns_jran.c create mode 100644 ami_test_client/libc-jss/udns/udns_misc.c create mode 100644 ami_test_client/libc-jss/udns/udns_parse.c create mode 100644 ami_test_client/libc-jss/udns/udns_resolver.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_a.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_mx.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_naptr.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_ptr.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_srv.c create mode 100644 ami_test_client/libc-jss/udns/udns_rr_txt.c create mode 100644 ami_test_client/libc-jss/utarray.h create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/LICENSE create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/README create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/Makefile create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/NOTES create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/ChangeLog.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/banner.png create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/banner.svg create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.png create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.svg create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/rss.png create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash-mini.png create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash-mini.svg create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash.png create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/index.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/license.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/styles.css create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh-quirks.css create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh.css create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/toc.css create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.html create mode 120000 ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.pdf create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/utarray.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/utlist.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/html/utstring.html create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/pdf/userguide.pdf create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/ChangeLog.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/sflogo.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/toc.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utarray.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utlist.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utstring.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/userguide.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utarray.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utlist.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utstring.txt create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/src/utarray.h create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/src/uthash.h create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/src/utlist.h create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/src/utstring.h create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/Makefile create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/README create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/all_funcs create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.c create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.sh create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.cygwin create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.mingw create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests_win32.cmd create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/emit_keys.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/example.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/hashscan.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/keystat.c create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/keystats create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/mexpand create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/simkeys.pl create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/sleep_test.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/tdiff.cpp create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test1.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test1.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test10.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test10.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test11.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test11.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test11.dat create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test12.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test12.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test13.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test13.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test14.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test14.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test14.dat create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test15.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test15.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test16.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test16.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test17.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test17.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test18.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test18.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test19.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test19.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test2.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test2.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test20.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test20.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test21.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test21.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test22.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test22.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test23.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test23.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test24.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test24.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test25.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test25.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test26.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test26.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test27.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test27.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test28.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test28.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test29.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test29.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test3.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test3.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test30.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test30.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test31.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test31.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test32.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test32.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test33.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test33.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test34.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test34.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test35.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test35.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test36.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test36.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test37.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test37.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test38.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test38.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test39.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test39.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test4.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test4.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test40.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test40.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test41.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test41.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test42.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test42.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test43.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test43.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test44.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test44.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test45.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test45.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test46.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test46.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test47.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test47.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test48.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test48.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test49.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test49.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test5.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test5.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test50.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test50.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test51.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test51.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test52.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test52.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test53.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test53.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test54.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test54.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test55.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test55.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test56.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test56.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test57.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test57.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test58.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test58.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test59.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test59.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test6.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test6.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test60.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test60.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test61.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test61.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test62.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test62.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test63.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test63.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test64.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test64.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test65.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test65.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test65.dat create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test66.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test66.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test67.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test67.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test7.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test7.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test8.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test8.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test9.ans create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/test9.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/threads/Makefile create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/threads/README create mode 100755 ami_test_client/libc-jss/uthash-1.9.6/tests/threads/do_tests create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test1.c create mode 100644 ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test2.c create mode 100644 ami_test_client/libc-jss/uthash.h create mode 100644 ami_test_client/libc-jss/utlist.h create mode 100644 ami_test_client/libc-jss/utstring.h create mode 100644 ami_test_client/milenage.c create mode 100644 ami_test_client/milenage.h create mode 100644 ami_test_client/originate.c create mode 100644 ami_test_client/originate.h create mode 100644 ami_test_client/volcon_config.h diff --git a/ami_test_client/.gitignore b/ami_test_client/.gitignore new file mode 100644 index 0000000000..0810a6d58f --- /dev/null +++ b/ami_test_client/.gitignore @@ -0,0 +1 @@ +ami_test_client diff --git a/ami_test_client/LICENSE b/ami_test_client/LICENSE new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/ami_test_client/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ami_test_client/Makefile b/ami_test_client/Makefile new file mode 100644 index 0000000000..d15b3dbc24 --- /dev/null +++ b/ami_test_client/Makefile @@ -0,0 +1,20 @@ +CC = gcc +CFLAGS = -Wall -ggdb -Ilibc-jss +LDFLAGS = -lev -lm -lcrypto + +OBJ=ami.o originate.o libc-jss/netsocket.o libc-jss/logger.o libc-jss/misc.o milenage.o +PROGS=ami_test_client + +.PHONY: all +all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) + +%: %.o $(OBJ) + gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) + +clean: + rm -f *.o $(OBJ) $(PROGS) + +volcon.o: volcon.c volcon_config.h + +install: compile + install -s volcon /usr/local/bin diff --git a/ami_test_client/NOTES.txt b/ami_test_client/NOTES.txt new file mode 100644 index 0000000000..b94292a40a --- /dev/null +++ b/ami_test_client/NOTES.txt @@ -0,0 +1,69 @@ +BUGOK: +- Asterisk 1.4 esetén az originate.c rossz Hangup-cause kódot ad vissza, ha Zap + csatornán indítjuk a hívást. Rendre 19-et ad vissza. Nem az originate.c + hibája, mert az AMI dumpban is 19 látszódik. Local csatornán rendben működik. + +TODO: +-első körben tesztelni kéne az ami.c-t a test_ami.c -vel. Írni valami kis célprogramot! pl. mellék állapot listázás +-ami.c-ben ami_event_list_t átnevezése valami kultúráltabb típusnévre, mert ez az API-ban megjelenik +-időzítőt betenni, hogy ha megszakad a kapcsolat az Asteriskkel, akkor ping timeout legyen +-időzítőt betenni, ami törlődik a sikeres authentikációnál +-Asterisk Call Manager/1.1 fejléc megérkezése előtt senki nem írhat az AMI socketre, mert ez lesz: + Asterisk Call Manager/1.1 + Response: Error + ActionID: 1 + Message: Missing action in request +-CLI response és az olyan több csomagos válaszok, mint pl. a DongleShowDevices kezelése +-ami_action-t ne lehessen regisztrálni, amíg nincs connected állapot + (vigyázni a beépített authentication ami_action-re!) +-ami_connect legyen hatástlan, ha már épp kapcsolódik vagy kapcsolódva van +-ami_event_unregister megírása +-ami dump events: response kulonvalasztasa +-event es response kulonvalasztasa keresesnel +-megvizsgálni, hogy az originate.c egyszerűbben működhet -e a linkedid változóval? +-Asterisk 1.4-et dobni, mert nem kell! + +CLI formátum: +action: command +command: sip show domains + +Response: Follows +Privilege: Command +SIP Domain support not enabled. + +--END COMMAND-- + + +SMS küldése + action: DongleSendPDU + ActionID: 59 + Device: dongle0 + PDU: 0031000B816002660304F000000010C8329BFD061DA74DD0F52D679343 + +Válasz a várakozósorból + Response: Success + ActionID: 59 + Message: [dongle0] SMS queued for send + ID: 0x5e13c0 + +Elküldve + Event: DongleSMSStatus + Privilege: call,all + Device: dongle0 + ID: 0x5e13c0 + Status: Sent + +Nincs elküldve + Event: DongleSMSStatus + Privilege: call,all + Device: dongle0 + ID: 0x5c0888 + Status: NotSent + + +JEGYZETEK +========= + +A linkedid változó, pl: ${CHANNEL(LinkedID)} hívásonként és nem csatornánként +változik. Tehát a tovább kapcsolt, illetve Local channel által továbbvitt +hívások ugyan azt a LinkedID-t kapják. diff --git a/ami_test_client/README.md b/ami_test_client/README.md new file mode 100644 index 0000000000..a5d7077a81 --- /dev/null +++ b/ami_test_client/README.md @@ -0,0 +1,76 @@ +# Asterisk Manager Interface client C library + +libamievent is an asynchronous event-driven client library for Asterisk Manager +Interface written in C. It uses [libev](http://software.schmorp.de/pkg/libev.html) as event +loop backend. + +With the libamievent you can send AMI commands and you can subscribe for +response to the command. When it arrives, the libamievent call the callback +function, which specified at subscription. The callback function allows you to +query AMI variables. + +libamievent support AMI events. You can also specify a callback function of +what the libamievent is called when events are received. + +Huge advantage of the libamievent, that the AMI commands and event names are +not hardcoded in the library. All commands, event names, parameters and +variables must be defined by printf-style strings with variable substitution. + +## Requirements + +* libev + +For Debian users: + + apt-get install libev-dev + +For Gentoo users: + + emerge -av libev + +## Install + +Currently, the Makefile is not prepared to carry out normal library. But, you +can build the example codes. + +Clone the libamievent repo with all submodules. + + git clone --recursive git://github.com/andrewjsi/libamievent + +Compile the source, test and install: + + make + +## Using library + +Sorry, the documentation still needs work, but in the meantime check out the sample +programs. + +## Todo, Future + +* more stability +* more examples +* build static and dynamic library +* well-written documentation +* the ability to be integrated with other systems as easy as possible + +## Source code + +The source code is well written as far as possible. We do not use tabs, instead +of 4 spaces is indented. All identifiers, including variables, function names +and macros written in English, but some comments and commits in Hungarian is, +because we are speaking and thinking in Hungarian. Nevertheless, we try to +write everything in English in the future. + +## Contribution + +It is an open source project, which is to be better and better. If you have any +ideas or find an error, or get stuck, you can contact us, please file a bug +report or send a pull-request! + +## License + +[_GPL2_](https://www.gnu.org/licenses/gpl-2.0.html) + +(C) Copyright 2010-2014 Andras Jeszenszky, [JSS & Hayer +IT](http://www.jsshayer.hu) All Rights Reserved. diff --git a/ami_test_client/ami.c b/ami_test_client/ami.c new file mode 100644 index 0000000000..11ac05ab5a --- /dev/null +++ b/ami_test_client/ami.c @@ -0,0 +1,864 @@ +/* ami.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +// debug infók (kommentezd, ha nem kell) +// #define CON_DEBUG + +// csomagok dumpolása stdout-ra (kommentezd, ha nem kell) +// #define AMI_DEBUG_PACKET_DUMP + +#include +#include +#include +#include +#include +#include // TODO: kell ez? +#include +#include // gettimeofday() + +#include "ami.h" +#include "debug.h" +#include "utlist.h" +#include "misc.h" +#include "logger.h" + +// event rögzítése +void put_event (ami_event_t *event) { + ami_event_t *event_copy = (ami_event_t*)malloc(sizeof(ami_event_t)); + if (!event_copy) { + conft("can't allocate event: %s, event lost", strerror(errno)); + return; + } + + //~ event tartalmának másolása az új event_copy számára lefoglalt területre. + //~ Emiatt lesz thread-safe a várakozó sor és a callback hívás. + memcpy(event_copy, event, sizeof(ami_event_t)); + + // bedobjuk a listába az új eseményt + DL_APPEND(event->ami->event_head, event_copy); + + //~ Esedékessé tesszük az azonnali (0 sec) időzítőt, aminek hatására az event + //~ loop a callback futtatások után azonnal meghívja az invoke_events() + //~ függvényt, ami meghivogatja a sorban álló eventekhez tartozó callback + //~ eljárásokat. Majd a multithread fejlesztésénél ezen a ponton az + //~ ev_timer_start() helyett az ev_async() függvénnyel kell jelezni a másik + //~ szálban futó event loop-nak, hogy dolog van. + ev_timer_start(event->ami->loop, &event->ami->need_event_processing); +} + +/* Felépítjük az event->field string tömböt, amiben az Asterisk által +küldött "változó: érték" párokat mentjük el úgy, hogy "változó", "érték", +"változó", "érték", ... A tömböt az ami->inbuf mutatókkal való +feldarabolásával és NULL byteok elhelyezésével kapjuk. + +A sorvégeket lezárhatja \r\n és \n is egyaránt. +A legutolsó sor végét nem kötelező lezárni. + +Ha az ami->inbuf tartalma: +Response: Success +Message: Authentication accepted + +Akkor az ami->field: +{"Respone","Success","Message","Authentication accepted"} + +field string tömb +max_field_size maximum ennyi darab string rakható a field tömbbe +field_len annyi lesz az értéke, ahány elem bekerült a field tömbbe +data innen olvassuk az adatokat és ezt a buffert daraboljuk fel és zárjuk le NULL-al +data_size data mérete +*/ + +//~ TODO: Hibás a függvény működése, ha a **field tömbünk mérete kicsi és a +//~ feldarabolás során nem férnek el benne a tokenek. Nincs segfault meg +//~ memóriahiba, hanem csak annyi történik, hogy az utolsó változó-érték pár +//~ értéke megkapja sortörésekkel együtt a maradék buffert. Ezt úgy lehetne +//~ megoldani, hogy a függvény nem bal-jobb oldalt vizsgál, hanem egy for ciklus +//~ NULL-ra állítja a ": " és a "\r" és "\n" karaktereket a teljes data-ban, majd +//~ csak ezután következne a feldarabolás mutatókkal. +//~ +//~ aug 21: A fent leirt hiba sz'tem nem hiba. +//~ +//~ aug 29: mi lenne, ha az utolsó változó-érték pár nem kapná meg a teljes buffert? +//~ vagy eleve netsocket_disconnect és feltakarítás kellene ide? +//~ +//~ A függvény nem kezeli azt az esetet, amikor az AMI változó-érték párnak nincs +//~ értéke és nincs a változó utáni kettőspont után szóköz, hanem egyből újsor +//~ karakter. Ilyen eset áll fenn az RTCPSent esemény ReportBlock változójában. +void tokenize_field (int *field, int max_field_size, int *field_len, char *data, int data_size) { + enum { + LEFT, + RIGHT, + } inexpr = LEFT; + + int len = 0; // visszatéréskor ezt mentjük el a *field_len -be + field[len++] = 0; // első pozíció a data legeleje, tehát 0 + int i; + + // összes \r karakter nullázása + for (i = 0; i < data_size; i++) + if (data[i] == '\r') + data[i] = '\0'; + + for (i = 0; i < data_size && len < max_field_size; i++) { + if (inexpr == LEFT) { // ": " bal oldalán vagyunk, változó + if (data[i] == ':' && data[i+1] == ' ') { + data[i] = '\0'; + data[i+1] = '\0'; + i += 2; + field[len++] = i; + inexpr = RIGHT; + } + } + + if (inexpr == RIGHT) { // ": " jobb oldalán vagyunk, érték + if (data[i] == '\n') { + data[i] = '\0'; + i += 1; + field[len++] = i; + inexpr = LEFT; + } + } + } + + if (inexpr == LEFT) + len--; + + *field_len = len; + + // AMI bal és jobb értékek dumpolása +#ifdef AMI_DEBUG_PACKET_DUMP + int z; + for (z = 0; z < len; z++) + printf("tokenize_field ### %d - (%s)\n", z, &data[field[z]]); + printf("\n"); +#endif +} + +static char *type2name (enum ami_event_type type) { + switch (type) { + case AMI_EVENT: return "EVENT" ; break; + case AMI_RESPONSE: return "RESPONSE" ; break; + case AMI_CLIRESPONSE: return "CLIRESPONSE" ; break; + case AMI_CONNECT: return "CONNECT" ; break; + case AMI_DISCONNECT: return "DISCONNECT" ; break; + default: return "UNKNOWN"; + } +} + +// belső esemény kiváltása (AMI_CONNECT, AMI_DISCONNECT, stb...) +static void generate_local_event (ami_t *ami, enum ami_event_type type, const char *fmt, ...) { + ami_event_t event_tmp; // ideiglenes event // TODO: ha működik, akkor bevezetni az ami->event_tmp helyett lent is + ami_event_t *event = &event_tmp; + bzero(event, sizeof(*event)); + + //~ char buf[AMI_BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(event->data, sizeof(event->data), fmt, ap); + va_end(ap); + event->data[AMI_BUFSIZ-1] = '\0'; // védelem // TODO: kell ez? + event->data_size = strlen(event->data); + +//~ printf("~~~ %s ~~~\n", event->data); + + tokenize_field( + event->field, + sizeof(event->field) / sizeof(char*) - 1, + &event->field_size, + event->data, + event->data_size + ); + + ami_event_list_t *el = NULL; + // végigmegyünk a regisztrált eseményeken + DL_FOREACH(ami->ami_event_list_head, el) { + if (el->type == type) { + event->callback = el->callback; + event->userdata = el->userdata; + event->regby_file = el->regby_file; + event->regby_line = el->regby_line; + event->regby_function = el->regby_function; + event->regby_cbname = el->regby_cbname; + event->regby_udname = el->regby_udname; + event->ami = ami; + event->type = el->type; + put_event(event); + } + } +} + +static void parse_cliresponse (ami_t *ami, int actionid, char *buf, int size) { + printf("***** CLI RESPONSE START *****\n"); + printf("***** ActionID = %d\n", actionid); + int i; + for (i = 0; i < size; i++) { + putchar(buf[i]); + } + printf("***** CLI RESPONSE END *****\n\n"); +} + +// bejövő Response es Event feldolgozása +static void parse_input (ami_t *ami, char *buf, int size) { + ami_event_t *event = &ami->event_tmp; + bzero(event, sizeof(*event)); + + memcpy(event->data, buf, size); + event->data_size = size; + + tokenize_field( + event->field, + sizeof(event->field) / sizeof(char*) - 1, + &event->field_size, + event->data, + size + ); + + char *var_response = ami_getvar(event, "Response"); + char *var_event = ami_getvar(event, "Event"); + /* * * RESPONSE * * */ + if (!strlen(var_event)) { + char *action_id_str = ami_getvar(event, "ActionID"); + if (action_id_str == NULL) { + con_debug("Missing ActionID in Response!"); + return; + } + event->action_id = atoi(action_id_str); + + if (!strcmp(var_response, "Success")) { + event->success = 1; + } else if (!strcmp(var_response, "Error")) { + event->success = 0; + } else { + con_debug("Unknown Response value: %s", var_response); + return; + } + + con_debug("RESPONSE - success = %d, action_id = %d", event->success, event->action_id); + + ami_event_list_t *el = NULL; + ami_event_list_t *eltmp = NULL; + DL_FOREACH_SAFE(ami->ami_event_list_head, el, eltmp) { + if (el->type != AMI_RESPONSE) // csak az AMI_RESPONSE típusú eseményeket vizsgáljuk + continue; + // event->action_id - Asterisktől érkezett ActionID + // el->action_id - adatbázisban szereplő ActionID + if (event->action_id == el->action_id) { + event->callback = el->callback; + event->userdata = el->userdata; + event->regby_file = el->regby_file; + event->regby_line = el->regby_line; + event->regby_function = el->regby_function; + event->regby_cbname = el->regby_cbname; + event->regby_udname = el->regby_udname; + event->ami = ami; + event->type = AMI_RESPONSE; + put_event(event); + DL_DELETE(ami->ami_event_list_head, el); + free(el); + return; + } + } + con_debug("Received ActionID=%d, but %d not found in ami_event_list_head!", event->action_id, event->action_id); + + /* * * EVENT * * */ + } else { +//~ printf("##### PARSE_INPUT EVENT #####\n"); + ami_event_list_t *el; + // végigmegyünk a regisztrált eseményeken + DL_FOREACH(ami->ami_event_list_head, el) { + if (el->type != AMI_EVENT) // csak az AMI_EVENT típusú eseményeket vizsgáljuk + continue; + // regisztrációban definiált változó=érték párok száma + int need_found = el->field_size / 2; // minden találatnál dekrementálva lesz +//~ printf(" REG need_found=%d allevents=%d by %s:%d\n", need_found, el->allevents, el->regby_file, el->regby_line); + if (need_found || el->allevents) { // ha van mit keresnünk + int n, i; + // végigmegyünk a regisztráció változó=érték párjain + for (n = 0; n < el->field_size; n += 2) { +//~ printf(" _reg_ %s=%s\n", &el->data[el->field[n]], &el->data[el->field[n+1]]); + // végigmegyünk a bejövő csomag változó=érték párjain + for (i = 0; i < event->field_size; i += 2) { +//~ printf(" _eve_ %s=%s\n", &event->data[event->field[i]], &event->data[event->field[i+1]]); + // ha egyezik a regisztrált változó neve a csomag változó nevével + if (!strcmp(&el->data[el->field[n]], &event->data[event->field[i]])) { + // ha egyezik a regisztrált változó értéke a csomag változó értékével + if (!strcmp(&el->data[el->field[n+1]], &event->data[event->field[i+1]])) { +//~ printf(" !found\n"); + need_found--; + } + } + } + } +//~ printf(" FIN need_found=%d\n", need_found); + // ha minden változó megtalálható volt és mindegyik értéke egyezett + // vagy "*" volt megadva a regisztrációnál (allevents) + if (need_found == 0 || el->allevents) { + event->callback = el->callback; + event->userdata = el->userdata; + event->regby_file = el->regby_file; + event->regby_line = el->regby_line; + event->regby_function = el->regby_function; + event->regby_cbname = el->regby_cbname; + event->regby_udname = el->regby_udname; + event->type = AMI_EVENT; + event->ami = ami; + put_event(event); + } + } + } + } +} + +void ami_disconnect (ami_t *ami, const char *fmt, ...) { + char reason_text[AMI_BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(reason_text, sizeof(reason_text), fmt, ap); + va_end(ap); + + if (netsocket_is_connected(ami->netsocket)) + netsocket_disconnect_withevent(ami->netsocket, reason_text); +} + +static void response_login (ami_event_t *response) { + ami_t *ami = response->ami; + + con_debug("auth reply: success=%d %s (by %s() %s:%d)", + response->success, + ami_getvar(response, "Message"), + response->regby_function, + response->regby_file, + response->regby_line + ); + + if (!response->ami->authenticated) { + if (response->success) { // AUTH accepted + response->ami->authenticated = 1; + // TODO: itt kell a connect timeout időzítőt törölni + generate_local_event(ami, + AMI_CONNECT, + "Host: %s\nIP: %s\nPort: %d", + ami->host, + ami->netsocket->ip, + ami->port); + } else { // AUTH failed + netsocket_disconnect_withevent(response->ami->netsocket, "Authentication failed"); + } + } +} + +static void process_input (ami_t *ami) { +#ifdef AMI_DEBUG_PACKET_DUMP + int pdi; + printf("----- NETSOCKET INBUF START -----\n"); + for (pdi = 0; pdi < ami->netsocket->inbuf_len; pdi++) + putchar(ami->netsocket->inbuf[pdi]); + printf("----- NETSOCKET INBUF END -----\n"); +#endif + +/* + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + E v e n t : D i a l \r \n \r \n A c t i o n + \r \n \r \n + + "Response: Follows\r\nPrivilege: Command\r\nActionID: %d\r\n" + "--END COMMAND--\r\n\r\n" +*/ + + // byte-onként végigmegyünk a netsocket bufferen + int i; + for (i = 0; i < ami->netsocket->inbuf_len; i++) { + // buffer overflow védelem + if (ami->inbuf_pos >= sizeof(ami->inbuf) - 1) { + printf("ELFOGYOTT A BUFFER!!!\n"); // TODO: netsocket_disconnect és feltakarítás + break; + } + + // byte másolása netsocketből ami->inbuf -ba + ami->inbuf[ami->inbuf_pos] = ami->netsocket->inbuf[i]; + + /* AMI header vizsgalata. Biztonsagi okokbol ha mar authentikalt + allapotban vagyunk, akkor ezt a vizsgalatot kihagyjuk */ + + // TODO. lekezelni azt az esetet, amikor kezdésnek nem ezt a fejlécet kapjuk! + if (!ami->authenticated && + (!strcmp(ami->inbuf, "Asterisk Call Manager/1.1\r\n") || + !strcmp(ami->inbuf, "Asterisk Call Manager/1.0\r\n") || + !strcmp(ami->inbuf, "Asterisk Call Manager/1.2\r\n") || + !strcmp(ami->inbuf, "Asterisk Call Manager/1.3\r\n"))) + { + bzero(ami->inbuf, sizeof(ami->inbuf)); + ami->inbuf_pos = 0; + con_debug("Received \"Asterisk Call Manager\" header, sending auth..."); + ami_action(ami, response_login, NULL, + "Action: Login\nUsername: %s\nSecret: %s\n", + ami->username, ami->secret); + bzero(ami->inbuf, sizeof(ami->inbuf)); + ami->inbuf_pos = 0; + return; + } + + // ha épp egy "Response: Follows" belsejében vagyunk + if (ami->cli_actionid > 0) { + // keressük a csomag végét + #define Q "--END COMMAND--\r\n\r\n" + #define QSIZE 19 + if (ami->inbuf_pos >= QSIZE - 1) { + if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) { + // megtaláltuk, mehet a feldolgozóba + parse_cliresponse(ami, ami->cli_actionid, ami->inbuf, ami->inbuf_pos - 18); + bzero(ami->inbuf, sizeof(ami->inbuf)); + ami->inbuf_pos = 0; + ami->cli_actionid = 0; + continue; + } + } + #undef Q + #undef QSIZE + + // folytatjuk tovább a beolvasást + ami->inbuf_pos++; + continue; + } + + /* Egy "Action: Command" csomagra (ami_cli() okozza) egy "Response: + Follows" válaszcsomag érkezik. Az ilyen csomagoknak speciális + formátuma van, ezért ezeket teljesen külön kell kezelni. Ezek a + csomagok a parse_input() helyett a parse_cliresponse() függvénynek + kerülnek át feldolgozásra. Az alábbi sscanf() megoldás megvizsgálja, + hogy az éppen beolvasás alatt álló csomag ilyen speciális "Response: + Follows" csomag lesz -e, illetve kiszedi belőle az ActionID-t. A + sscanf() az ami->inbuf baloldali illeszkedését vizsgálja és ha + tudja, akkor az ami->cli_actionid változóba menti el a kapott + ActionID-t. */ + if (sscanf(ami->inbuf, "Response: Follows\r\nPrivilege: Command\r\nActionID: %d\r\n", &ami->cli_actionid) > 0) { + // az ami->inbuf jobboldali illeszkedését az strncmp()-vel vizsgáljuk + #define Q "\r\n" + #define QSIZE 2 // Q mérete + if (ami->inbuf_pos >= QSIZE - 1) { + // ha illeszkedik jobbról a \r\n, akkor tovább olvassuk az adatokat + if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) { + bzero(ami->inbuf, sizeof(ami->inbuf)); + ami->inbuf_pos = 0; + continue; // ezen a ponton az ami->cli_actionid -ben ott figyel az ActionID + } + } + #undef Q + #undef QSIZE + + /* Ide akkor kerülünk, ha a sscanf() (bal oldal) illeszkedett, de az + strncmp() (jobb oldal) nem. Ebben az esetben elképzelhető, hogy + a sscanf() hibásan kiolvassa az ActionID egy töredékét, ezért + biztos ami biztos, kinullázzuk. ezen a ponton lehetséges, hogy a + sscanf() az ActinID csak egy töredékét szedte ki, ezért nullázunk. */ + ami->cli_actionid = 0; + + // folytatjuk tovább a beolvasást + ami->inbuf_pos++; + continue; + } + + /* Ha van elég adat, hogy az ami->inbuf -ban 3 byte-ot visszaléphessünk, + akkor megvizsgáljuk, hogy vajon éppen egy csomag lezárásánál állunk + -e, azaz az ami->inbuf legutolsó 4 byte-ja megegyezik ezzel: + "\r\n\r\n". Ha igen, akkor az azt jelenti, hogy az ami->inbuf pont + egy teljes csomagot tartalmaz, amit elküldünk a parse_input()-nak, + majd kinullázzuk a teljes ami->inbuf buffert és az ami->inbuf_pos + pozicionáló változót. Ezután folytatjuk a következő csomag byte- + onkénti olvasását. Ha már nincs a netsocket->inbuf -ban feldolgozandó + cucc, akkor a for ciklus kilép és majd a következő körben + folytatódik az olvasás. */ + + /* TODO: Logikailag nem korrekt ez a megoldás! Segfault veszély + ami_event_unregister() után. A process_input() és parse_input() + páros a teljes netsocket->inbuf -ban lévő cuccot egyetlen egy körben + feldolgozza. Ebben egyszerre több csomag is lehet. Az érdekes + eseményeket a put_event() a saját listájába tolja és csak a + következő körben lesz callback hívás. Tegyük fel, hogy egyszerre 2 + csomag érkezik. Az első csomagban lévő esemény callback függvénye + megrendel egy új eseményt, amire történetesen pont a második csomag + illeszkedne. De mivel a megrendelés előtt már megtörtént az + összehasonlítás, szűrés és a futtatandó események kiválasztása, + ezért erről a második eseményről le fog maradni a hívó. Másképpen + szólva egy-egy megrendelésnek (vagy lemondásnak) csak a teljes + put_event() által karbantartott lista (callback lista) lefuttatása + után lesz hatása. Ez eseményről való lemaradást okozhat, illetve + lemondásnál segfaultot is, ugyanis ha történik egy + ami_event_unregister() akkor ezután még a put_event() által + karbantartott listából lefuthat a (már lemondott) callback. Ötlet: + valami olyan megoldás kéne, hogy még itt a process_input / + parse_input szintjén ha fennakad a szűrőn egy esemény, akkor a + put_event() regisztráció után álljon le a parse_input() és az event + loop hívja meg a need_event_processing-et. És majd csak ezután + folytatódjon a parse_input() vizsgálódása. Vaaagy... egy merészebb + ötlet. A bejövő AMI buffer visszamenőleg addig legyen eltárolva, + amíg az invoke_events még foglalkozik a callback hívásokkal. + Multithread környezetben az invoke_events a megrendelő szálában fog + futni. Elképzelhető, hogy az invoke_events-ből kellene vizsgálni + azt, hogy az éppen bejövő AMI eseményt kell -e futtatni. */ + + #define Q "\r\n\r\n" + #define QSIZE 4 + if (ami->inbuf_pos >= QSIZE - 1) { + if (!strncmp(Q, &ami->inbuf[ami->inbuf_pos - (QSIZE - 1)], QSIZE)) { + parse_input(ami, ami->inbuf, ami->inbuf_pos - 1); // -1 azért, hogy ne menjen át a legutolsó \r\n-ből az \r (érthető?) + bzero(ami->inbuf, sizeof(ami->inbuf)); + ami->inbuf_pos = 0; + continue; // ne jusson el az ami->inbuf_pos++ -ig :) + } + } + #undef Q + #undef QSIZE + ami->inbuf_pos++; + } +} + +static void netsocket_callback (netsocket_t *netsocket, int event) { + ami_t *ami = netsocket->userdata; + int was_authenticated = 0; + + switch (event) { + case NETSOCKET_EVENT_CONNECT: + con_debug("Connected to %s (%s) port %d", + netsocket->host, + netsocket->ip, + netsocket->port + ); + break; + + case NETSOCKET_EVENT_DISCONNECT: + was_authenticated = ami->authenticated; + + // TODO: itt kell alaphelyzetbe állítani az ami-t. + // disconnect esemény szétkűrtölése előtt + ami->authenticated = 0; + + generate_local_event(ami, + AMI_DISCONNECT, + "Host: %s\nIP: %s\nPort: %d\nReason: %s\nWasAuthenticated: %d", + netsocket->host, + (netsocket->ip) ? netsocket->ip : "", + netsocket->port, + netsocket->disconnect_reason, + was_authenticated + ); + + if (netsocket->connected) { + con_debug("Disconnected from %s: %s", + netsocket->host, + netsocket->disconnect_reason + ); + } else { + con_debug("Can't connect to %s[%s]:%d %s", + netsocket->host, + (netsocket->ip) ? netsocket->ip : "", + netsocket->port, + netsocket->disconnect_reason + ); + } + break; + + case NETSOCKET_EVENT_READ: + process_input(ami); + break; + } +} + +// hívja az ami->need_event_processing azonnali időzítő +static void invoke_events (EV_P_ ev_io *w, int revents) { + ami_t *ami = w->data; + + ami_event_t *event, *tmp; + DL_FOREACH_SAFE(ami->event_head, event, tmp) { + if (event->callback != NULL) { + con_debug("call %s()", event->regby_cbname); + event->callback(event); + con_debug("end %s()", event->regby_cbname); + } + DL_DELETE(ami->event_head, event); + free(event); + } +} + +// 6 byte-os random hexa stringet masol az ami->uuid bufferbe +// TODO: egy rendes, unique ID-t visszaado fuggvenyt irni ehelyett a random vacak helyett +// pl. az util-linux-ng csomagban levo libuuid segitsegevel +static void generate_uuid (char *dst, size_t size) { + struct timeval tv; + int num; + char tmp[16]; + + gettimeofday(&tv, NULL); + srand(tv.tv_usec * tv.tv_sec); + num = rand(); + snprintf(tmp, sizeof(tmp), "%x", num); + tmp[6] = '\0'; + strncpy(dst, tmp, size); +} + +void connect_delayed (EV_P_ ev_io *w, int revents) { + ami_t *ami = w->data; + con_debug("invoked connect by timer"); + ev_timer_stop(ami->loop, &ami->t_connect_delayed); + ami_connect(ami); +} + +// delay: millisec +void ami_connect_delayed (ami_t *ami, int delay) { + con_debug("connect after %d ms ...", delay); + ev_timer_stop(ami->loop, &ami->t_connect_delayed); + ev_timer_set(&ami->t_connect_delayed, (float)((float)delay / (float)1000), 0); + ev_timer_start(ami->loop, &ami->t_connect_delayed); +} + +ami_t *ami_new (struct ev_loop *loop) { + ami_t *ami = malloc(sizeof(*ami)); + if (ami == NULL) { + con_debug("ami_new() returned NULL"); + return NULL; + } + bzero(ami, sizeof(*ami)); // minden NULL + + // AMI UUID + generate_uuid(ami->uuid, sizeof(ami->uuid)); + + // ha meg van adva a loop paraméter, akkor azt használjuk eseménykezelőnek + // ellenkező esetben az alapértelmezett eseménykezelőt + ami->loop = (loop != NULL) ? loop : ev_default_loop(0); + + // default értékek + strncpy(ami->host, AMI_DEFAULT_HOST, sizeof(ami->host) - 1); + ami->port = AMI_DEFAULT_PORT; + + if (!(ami->netsocket = netsocket_new(netsocket_callback, ami, ami->loop))) { + con_debug("netsocket_new returned NULL"); + } + netsocket_host(ami->netsocket, AMI_DEFAULT_HOST); + netsocket_port(ami->netsocket, AMI_DEFAULT_PORT); + + ami->need_event_processing.data = ami; // ami objektum így kerül az invoke_events-be + ev_timer_init(&ami->need_event_processing, (void*)invoke_events, 0, 0); + + ami->t_connect_delayed.data = ami; + ev_timer_init(&ami->t_connect_delayed, (void*)connect_delayed, 0, 0); + + return ami; +} + +void ami_destroy(ami_t *ami) { + netsocket_destroy(ami->netsocket); +} + +void ami_credentials (ami_t *ami, const char *username, const char *secret, const char *host, const char *port) { + if (username != NULL) + strncpy(ami->username, username, sizeof(ami->username) - 1); + + if (secret != NULL) + strncpy(ami->secret, secret, sizeof(ami->secret) - 1); + + if (host != NULL) + strncpy(ami->host, host, sizeof(ami->host) - 1); + + if (port != NULL) { + int port_tmp = atoi(port); + if (port_tmp > 0 || port_tmp < 65536) + ami->port = port_tmp; + } +} + +void ami_connect (ami_t *ami) { + netsocket_host(ami->netsocket, ami->host); + netsocket_port(ami->netsocket, ami->port); + netsocket_connect(ami->netsocket); +} + +int ami_printf (ami_t *ami, const char *fmt, ...) { + char buf[AMI_BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + int field[AMI_FIELD_SIZE]; + int field_size; + tokenize_field( + field, + sizeof(field) / sizeof(char*) - 1, + &field_size, + buf, + strlen(buf) + ); + + char packet[AMI_BUFSIZ]; + int i; + strcpy(packet, ""); + for (i = 0; i < field_size; i += 2) + concatf(packet, "%s: %s\r\n", &buf[field[i]], &buf[field[i+1]]); + concat(packet, "\r\n"); + + if (ami->netsocket != NULL) { +#ifdef AMI_DEBUG_PACKET_DUMP + printf("----- NETSOCKET WRITE START ------\n"); + printf("%s", packet); + printf("----- NETSOCKET WRITE END ------\n"); +#endif + return netsocket_printf(ami->netsocket, "%s", packet); + } else { + return -1; + } +} + +ami_event_list_t *_ami_action (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...) { + char buf[AMI_BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + buf[AMI_BUFSIZ-1] = '\0'; // védelem + + if (callback != NULL) { + ami_event_list_t *el = malloc(sizeof(ami_event_list_t)); + bzero(el, sizeof(*el)); // NULL, NULL, NULL :) + el->callback = callback; + el->userdata = userdata; + el->type = AMI_RESPONSE; + el->regby_file = file; + el->regby_line = line; + el->regby_function = function; + el->regby_cbname = cbname; + el->regby_udname = udname; + ami->action_id++; // új ActionID + el->action_id = ami->action_id; + ami_printf(ami, "ActionID: %d\n%s", ami->action_id, buf); + con_debug("registered action #%d, callback: %s()", el->action_id, el->regby_cbname); + DL_APPEND(ami->ami_event_list_head, el); + return el; + } else { + ami_printf(ami, "%s", buf); + return NULL; + } +} + +ami_event_list_t *_ami_event_register (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...) { + ami_event_list_t *el = malloc(sizeof(ami_event_list_t)); + bzero(el, sizeof(*el)); // NULL, NULL, NULL :) + + va_list ap; + va_start(ap, fmt); + vsnprintf(el->data, sizeof(el->data), fmt, ap); + va_end(ap); + + el->callback = callback; + el->userdata = userdata; + el->regby_file = file; + el->regby_line = line; + el->regby_function = function; + el->regby_cbname = cbname; + el->regby_udname = udname; + + // belső esemény: Connect + if (!strcmp(el->data, "Connect")) { + el->type = AMI_CONNECT; + + // belső esemény: Disconnect + } else if (!strcmp(el->data, "Disconnect")) { + el->type = AMI_DISCONNECT; + + // Minden Asterisk esemény szűrés nélkül + } else if (!strcmp(el->data, "*")) { + el->type = AMI_EVENT; + el->allevents = 1; + + // Asterisk esemény, feltételek feldarabolása + } else { + el->type = AMI_EVENT; + tokenize_field( + el->field, + sizeof(el->field) / sizeof(char*) - 1, + &el->field_size, + el->data, + sizeof(el->data) + ); + } + + DL_APPEND(ami->ami_event_list_head, el); + con_debug("EVENT registered, callback: %s by %s() in %s line %d", + el->regby_cbname, el->regby_function, el->regby_file, el->regby_line); + + return el; +} + +void ami_event_unregister(ami_t *ami, ami_event_list_t *el) { + if (el == NULL) { + con_debug("attempting to unregister NULL pointer event!"); + return; + } + con_debug("EVENT unregistered, callback: %s()", el->regby_cbname); + DL_DELETE(ami->ami_event_list_head, el); + free(el); +} + +void ami_event_dump (ami_event_t *event) { + printf( + "Incoming %s /0x%lx/\n" + " Registered by %s() in %s line %d\n" + " Callback: %s() /0x%lx/, Userdata: %s /0x%lx/\n" + " success=%d action_id=%d data_size=%d field_size=%d\n" + , type2name(event->type), (unsigned long)event + , event->regby_function, event->regby_file, event->regby_line + , event->regby_cbname, (unsigned long)event->callback, event->regby_udname, (unsigned long)event->userdata + , event->success, event->action_id, event->data_size, event->field_size + ); + int i; + for (i = 0; i < event->field_size; i += 2) + printf(" %-16s %s\n", &event->data[event->field[i]], &event->data[event->field[i+1]]); + printf("\n"); +} + +void ami_dump_event_list_element (ami_event_list_t *el) { + printf( + "Registered %s /0x%lx/\n" + " Registered by %s() in %s line %d\n" + " Callback: %s() /0x%lx/, Userdata: %s /0x%lx/\n" + " action_id=%d field_size=%d\n" + , type2name(el->type), (unsigned long)el + , el->regby_function, el->regby_file, el->regby_line + , el->regby_cbname, (unsigned long)el->callback, el->regby_udname, (unsigned long)el->userdata + , el->action_id, el->field_size + ); + int i; + for (i = 0; i < el->field_size; i += 2) + printf(" %-16s %s\n", &el->data[el->field[i]], &el->data[el->field[i+1]]); + printf("\n"); +} + +void ami_dump_lists (ami_t *ami) { + printf("** REGISTERED AMI EVENTS **\n"); + ami_event_list_t *el; + DL_FOREACH(ami->ami_event_list_head, el) + ami_dump_event_list_element(el); +} + +char *ami_getvar (ami_event_t *event, char *var) { + int i; + for (i = 0; i < event->field_size; i += 2) { + if (!strcmp(&event->data[event->field[i]], var)) { + if (&event->data[event->field[i+1]] != NULL) { + return &event->data[event->field[i+1]]; + } else { + return ""; + } + } + } + return ""; +} + +void ami_strncpy (ami_event_t *event, char *dest, char *var, size_t maxsize) { + +} + diff --git a/ami_test_client/ami.h b/ami_test_client/ami.h new file mode 100644 index 0000000000..7e8e50e83c --- /dev/null +++ b/ami_test_client/ami.h @@ -0,0 +1,135 @@ +/* ami.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef AMI_H_INCLUDED +#define AMI_H_INCLUDED + +#include "netsocket.h" + +// TODO: doksiba AMI_DEBUG_PACKET_DUMP + +#ifndef AMI_DEFAULT_HOST +#define AMI_DEFAULT_HOST "localhost" +#endif + +#ifndef AMI_DEFAULT_PORT +#define AMI_DEFAULT_PORT 5038 +#endif + +#ifndef AMI_BUFSIZ +#define AMI_BUFSIZ 4096 +#endif + +#ifndef AMI_FIELD_SIZE +#define AMI_FIELD_SIZE 300 +#endif + +enum ami_event_type { + AMI_EVENT = 1, + AMI_RESPONSE, + AMI_CLIRESPONSE, + AMI_CONNECT, + AMI_DISCONNECT, +}; + +// ha változik, akkor egyeztess az ami.c ami_dump_lists() függvénnyel! +typedef struct ami_event_list_t { + struct ami_event_list_t *prev; + struct ami_event_list_t *next; + void (*callback)(void*); + void *userdata; + int field[AMI_FIELD_SIZE]; + int field_size; + char data[AMI_BUFSIZ]; + char *regby_file; + int regby_line; + const char *regby_function; + const char *regby_cbname; + const char *regby_udname; + unsigned int action_id; + enum ami_event_type type; + int allevents; // 1 lesz, ha a megrendelo szures nelkul az osszes AMI esemenyt keri +} ami_event_list_t; + +typedef struct ami_event_t { + struct ami_event_t *prev; + struct ami_event_t *next; + struct ami_t *ami; + int success; // csak "Response: Success" esetén lesz egy, tehát biztos hogy volt Response és az értéke Success volt + int field[AMI_FIELD_SIZE]; + int field_size; + char data[AMI_BUFSIZ]; + int data_size; + void (*callback)(void*); + void *userdata; + unsigned int action_id; + char *regby_file; + int regby_line; + const char *regby_function; + const char *regby_cbname; + const char *regby_udname; + enum ami_event_type type; +} ami_event_t; + +typedef struct ami_t { + char host[64]; // Asterisk host + int port; // Asterisk Manager Interface port + char username[32]; // AMI User + char secret[32]; // AMI Password + netsocket_t *netsocket; // Netsocket objektum + char disconnect_reason[64]; // ??? + ami_event_list_t *ami_event_list_head; // megrendelt események + struct ev_loop *loop; // eseményhurok + ev_timer need_event_processing; // azonnali időzítő az események kiküldéséhez + ev_timer t_connect_delayed; // késleltetett connect időzítő + char inbuf[AMI_BUFSIZ]; // bejövő buffer + int inbuf_pos; // bejövő buffer pozíciója + struct ami_event_t *event_head; // esemény várakozósor + struct ami_event_t event_tmp; // itt készítünk új eseményt, amit aztán a várakozósorba másolunk + int authenticated; // 1 lesz sikeres login után + unsigned int action_id; // soron következő használható ActionID + char uuid[16]; // AMI sajat belso ID + int cli_actionid; // process_input() itt jegyzi meg a Response: Follows ActionID-t +} ami_t; + +ami_t *ami_new (struct ev_loop *loop); + +void ami_credentials (ami_t *ami, const char *username, const char *secret, const char *host, const char *port); + +void ami_destroy(ami_t *ami); + +void ami_connect (ami_t *ami); + +void ami_connect_delayed (ami_t *ami, int delay); + +int ami_printf (ami_t *ami, const char *fmt, ...); + +#define ami_action(ami, callback, userdata, ...) \ + _ami_action(ami, callback, userdata, __FILE__, __LINE__, __FUNCTION__, #callback, #userdata, __VA_ARGS__) +ami_event_list_t *_ami_action (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...); + +#define ami_event_register(ami, callback, userdata, ...) \ + _ami_event_register(ami, callback, userdata, __FILE__, __LINE__, __FUNCTION__, #callback, #userdata, __VA_ARGS__) +ami_event_list_t *_ami_event_register (ami_t *ami, void *callback, void *userdata, char *file, int line, const char *function, const char *cbname, const char *udname, const char *fmt, ...); + +void ami_event_unregister(ami_t *ami, ami_event_list_t *el); + +char *ami_getvar (ami_event_t *event, char *var); + +#define ami_strcpy(event,dest,var) ami_strncpy(event,dest,var,sizeof(dest)) + +void ami_strncpy (ami_event_t *event, char *dest, char *var, size_t maxsize); + +void ami_dump_lists (ami_t *ami); + +void ami_event_dump (ami_event_t *el); + +void ami_disconnect (ami_t *ami, const char *fmt, ...); + +#endif // #ifndef AMI_H_INCLUDED + diff --git a/ami_test_client/ami_test_client.c b/ami_test_client/ami_test_client.c new file mode 100644 index 0000000000..133d75c550 --- /dev/null +++ b/ami_test_client/ami_test_client.c @@ -0,0 +1,279 @@ + +#include +#include +#include +#include +#include + +#include "ami.h" +#include "debug.h" +#include "utlist.h" + +#define CON_DEBUG +#include "logger.h" + +#include "milenage.h" + +ev_timer timer; +ev_timer timer_reconnect; + +ami_t *ami; +int dstatus_n = 2; + +//~ static void ami_event_callback (ami_event_t *ame) { + //~ char *userdata = (char*)ame->userdata; + //~ char *status = ami_getvar(ame, "Status"); + //~ + //~ char status2[64]; + //~ strncpy(status2, ami_getvar(ame, "Status"), 64); + //~ ami_strncpy(ame, status2, "Status", 64); +//~ + //~ ami_event_unregister(ame); +//~ } + +void utproba () { + typedef struct st { + struct st *prev; + struct st *next; + char str[32]; + } st; + + st *head = NULL; + st *el; + st *x10, *x15; + + int i; + for (i = 0; i < 20; i++) { + el = (st*)malloc(sizeof(*el)); + bzero(el, sizeof(*el)); + + sprintf(el->str, "str %d", i); + if (i == 10) + x10 = el; + if (i == 15) + x15 = el; + DL_APPEND(head, el); + } + + st *eltmp; + DL_FOREACH_SAFE(head, el, eltmp) { + if (el == x10) { + st *csere = (st*)malloc(sizeof(st)); + strcpy(csere->str, "csere1"); + DL_APPEND(head, csere); + csere = (st*)malloc(sizeof(st)); + strcpy(csere->str, "csere2"); + DL_APPEND(head, csere); + + DL_DELETE(head, el); + free(el); + } + + if (el == x15) { + DL_DELETE(head, el); + free(el); + } + + } + + DL_FOREACH_SAFE(head, el, eltmp) { + printf("s %s s\n", el->str); + DL_DELETE(head, el); + free(el); + } + + return; +} + +int do_register = 0; + +void event_response (ami_event_t *event) { + const char *message_str = ami_getvar(event, "Message"); + + printf("response: %s\n", message_str); +} + +void event_fullybooted (ami_event_t *event) { + printf("*** Fully booted ***\n"); + if (do_register) + return; + do_register = 1; + ami_action(ami, event_response, NULL, "Action: PJSIPRegister\nRegistration: volte_ims"); +} + +int hex_to_octet_string(const char *name, const char *input, uint8_t *output, size_t output_size) +{ + int i, n; + + if (!input || !input[0]) { + printf("Missing value for hex string '%s'.\n", name); + return -1; + } + i = n = 0; + while (*input) { + if (i == output_size) { + printf("Value for hex string '%s' too long, expecting %zu bytes.\n", name, + output_size); + return -1; + } + if (*input >= '0' && *input <= '9') + output[i] = (output[i] << 4) | (*input - '0'); + else if (*input >= 'a' && *input <= 'f') + output[i] = (output[i] << 4) | (*input - 'a' + 10); + else if (*input >= 'A' && *input <= 'F') + output[i] = (output[i] << 4) | (*input - 'A' + 10); + else { + printf("Value for hex string '%s' has invalid character '%c'.\n", name, *input); + return -1; + } + if (++n == 2) { + n = 0; + i++; + } + input++; + } + if (i < output_size) { + printf("Value for hex string '%s' too short, expecting %zu bytes.\n", name, output_size); + return -1; + } + + return 0; +} + +void octest_string_to_hex(uint8_t *input, size_t input_size, char *output) +{ + while(input_size) { + sprintf(output, "%02x", *input); + input++; + input_size--; + output += 2; + } +} + +static const char *usim_opc = "775A1F887D2AD66F9719C2C79F847B50"; +static const char *usim_k = "D534E07854B75E475C667A856AA31F9C"; +static const char *usim_sqn = "000000011000"; + +void event_authrequest (ami_event_t *event) { + const char *rand_str, *autn_str; + uint8_t opc[16], k[16], sqn[6], rand[16], autn[16], res[8], ik[16], ck[16], auts[14]; + char res_str[256], ik_str[256], ck_str[256], auts_str[256]; + size_t res_len = 8; + rand_str = ami_getvar(event, "RAND"); + autn_str = ami_getvar(event, "AUTN"); + int rc; + + if (!rand_str || !autn_str) { + printf("Missing parameters in AuthRequest\n"); + return; + } + + if (hex_to_octet_string("RAND", rand_str, rand, sizeof(rand))) { + printf("Invalid RAND parameter in AuthRequest\n"); + return; + } + if (hex_to_octet_string("AUTN", autn_str, autn, sizeof(autn))) { + printf("Invalid AUTN parameter in AuthRequest\n"); + return; + } + if (hex_to_octet_string("OPC", usim_opc, opc, sizeof(opc))) { + printf("Invalid OPC parameter in AuthRequest\n"); + return; + } + if (hex_to_octet_string("K", usim_k, k, sizeof(k))) { + printf("Invalid K parameter in AuthRequest\n"); + return; + } + if (hex_to_octet_string("SQN", usim_sqn, sqn, sizeof(sqn))) { + printf("Invalid SQN parameter in AuthRequest\n"); + return; + } + rc = milenage_check(opc, k, sqn, rand, autn, ik, ck, res, &res_len, auts); + octest_string_to_hex(res, sizeof(res), res_str); + octest_string_to_hex(ik, sizeof(ik), ik_str); + octest_string_to_hex(ck, sizeof(ck), ck_str); + octest_string_to_hex(auts, sizeof(auts), auts_str); + if (rc == -2) { + printf("*** Sending AuthResponse with authentication token ***\n"); + ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims\nAUTS: %s", auts_str); + } else if (!rc) { + printf("*** Sending AuthResponse with authentication result and keys ***\n"); + ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims\nRES: %s\nCK: %s\nIK: %s", res_str, ck_str, ik_str); + } else { + printf("*** Sending AuthResponse with authentication error\n"); + ami_action(ami, NULL, NULL, "Action: AuthResponse\nRegistration: volte_ims"); + } + +} + +void reconnect_ami (ami_event_t *event) { + printf("*** Reconnecting... ***\n"); + ev_timer_stop(EV_DEFAULT, &timer_reconnect); + ami_connect(ami); +} + +void event_connect (ami_event_t *event) { + printf("*** CONNECTED ***\n"); + printf(" semmi = %s\n host = %s\n ip = %s\n port = %s\n", + ami_getvar(event, "semmi"), + ami_getvar(event, "Host"), + ami_getvar(event, "IP"), + ami_getvar(event, "Port")); +} + +void event_disconnect (ami_event_t *event) { + printf("*** DISCONNECTED ***\n"); + printf(" reason = %s\n host = %s\n ip = %s\n port = %s\n", + ami_getvar(event, "Reason"), + ami_getvar(event, "Host"), + ami_getvar(event, "IP"), + ami_getvar(event, "Port")); + ev_timer_again(EV_DEFAULT, &timer_reconnect); + ev_timer_start(EV_DEFAULT, &timer_reconnect); +} + +int main (int argc, char *argv[]) { + ami = ami_new(EV_DEFAULT); + if (ami == NULL) { + con_debug("ami_new() returned NULL"); + return 1; + } + + char host[128]; + + if (argc < 2) { + //~ printf("usage: %s \n", argv[0]); + printf("Default host used!\n"); + strcpy(host, "127.0.0.1"); + } else { + strcpy(host, argv[1]); + } + + printf("Connecting to %s ...\n", host); + ami_credentials(ami, "jolly", "svenja", host, "5038"); + ami_connect(ami); + + ami_event_register(ami, event_fullybooted, NULL, "Event: FullyBooted"); + + ev_timer_init(&timer_reconnect, (void*)reconnect_ami, 3, 3); + //~ ev_timer_start(EV_DEFAULT, &timer_reconnect); + + ami_event_register(ami, event_connect, NULL, "Connect"); + ami_event_register(ami, event_disconnect, NULL, "Disconnect"); + + ami_event_register(ami, event_authrequest, NULL, "Event: AuthRequest"); + + printf("\n"); + ami_dump_lists(ami); + + ami_action(ami, NULL, NULL, "Action: Login\nUsername: jolly\nSecret: svenja"); + + ev_loop(EV_DEFAULT, 0); + + ami_destroy(ami); + + return 0; +} + + + diff --git a/ami_test_client/libc-jss/API_CHANGES.txt b/ami_test_client/libc-jss/API_CHANGES.txt new file mode 100644 index 0000000000..5102e58052 --- /dev/null +++ b/ami_test_client/libc-jss/API_CHANGES.txt @@ -0,0 +1,30 @@ +2013-09-17 - netsocket +========== + +A netsocket mostantól nem fogad el kívülről értékeket, tehát nem szabad +közvetlenül a programból manipulálni a netsocket_t objektumot. A host, lhost, +port, lport értékeket az alábbi függvények segítségével lehet beállítani. Ennek +oka az, hogy eddig a netsocket_t mutatókban tárolta a host és lhost stringeket, +ami azt okozta, hogy ha a főprogramban megváltoztattuk a hostot, akkor a +netsocket-ben is megváltozott, hisz ugyan arra mutattak. Mostantól a +netsocket_t saját char[] tömbben tárolja ezeket az értékeket, így nem lesz +hatással semmilyen külső változtatás, illetve nem fog összekeveredni. + +Eddig így kellett hívni: + +netsocket->host = host; +netsocket->port = port; +netsocket->lhost = lhost; +netsocket->lport = lport; + +Mostantól pedig így kell: + +netsocket_host(netsocket, host); +netsocket_port(netsocket, port); +netsocket_lhost(netsocket, lhost); +netsocket_lport(netsocket, lport); + + + + + diff --git a/ami_test_client/libc-jss/Doxyfile b/ami_test_client/libc-jss/Doxyfile new file mode 100644 index 0000000000..13727962e6 --- /dev/null +++ b/ami_test_client/libc-jss/Doxyfile @@ -0,0 +1,1510 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "JSS Corridor Netsocket" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/ami_test_client/libc-jss/LICENSE b/ami_test_client/libc-jss/LICENSE new file mode 100644 index 0000000000..d159169d10 --- /dev/null +++ b/ami_test_client/libc-jss/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/ami_test_client/libc-jss/Makefile b/ami_test_client/libc-jss/Makefile new file mode 100644 index 0000000000..ffe97883bc --- /dev/null +++ b/ami_test_client/libc-jss/Makefile @@ -0,0 +1,29 @@ + +CC = gcc +CFLAGS = -Wall +LIBS = -lcrypto -lev +OBJ=pipe.o netsocket.o +BIN=pipe + +all: logger pipe fmtsub_test + +pipe: $(OBJ) + $(CC) $(CFLAGS) -o $(BIN) $(OBJ) $(EXTOBJ) $(LIBS) + +logger: logger.o logger_test.o misc.o + $(CC) $(CFLAGS) -o logger logger.o logger_test.o misc.o + +fmtsub_test: misc.o fmtsub_test.o + $(CC) $(CFLAGS) -o fmtsub_test misc.o fmtsub_test.o + +doc: + doxygen + @echo "" + @echo "DOC URL: file://"`pwd`"/html/index.html" + @echo "" + +clean: + rm -rf $(OBJ) $(BIN) logger fmtsub_test core *.o html/ latex/ man/ + +# Függőségek +#policy.o: server.h diff --git a/ami_test_client/libc-jss/NOTES.txt b/ami_test_client/libc-jss/NOTES.txt new file mode 100644 index 0000000000..59e2933768 --- /dev/null +++ b/ami_test_client/libc-jss/NOTES.txt @@ -0,0 +1,15 @@ +Ring Bufferrel megoldhato a mem logging? +http://en.wikipedia.org/wiki/Circular_buffer + + +LIBEV STRICT ALIASING +===================== + +netsocket.c -ben a libev örjöng: + +* libjsi/netsocket.c: In function ‘netsocket_connect’: +* libjsi/netsocket.c:264: warning: dereferencing type-punned pointer will break strict-aliasing rules + +A megoldás egyelőre az, hogy -fno-strict-aliasing CFLAGS-et használjuk. + + diff --git a/ami_test_client/libc-jss/README.md b/ami_test_client/libc-jss/README.md new file mode 100644 index 0000000000..8cf649eceb --- /dev/null +++ b/ami_test_client/libc-jss/README.md @@ -0,0 +1,28 @@ +# JSS C Library + +This library contains the C functions and macros, which are often used in our +projects. In the future, this will be a stand-alone library with unit tests and +documentation. + +[AMISMS](https://github.com/andrewjsi/amisms) and [libamievent](https://github.com/andrewjsi/libamievent) uses this library. + +## Source code + +The source code is well written as far as possible. We do not use tabs, instead +of 4 spaces is indented. All identifiers, including variables, function names +and macros written in English, but some comments and commits in Hungarian is, +because we are speaking and thinking in Hungarian. Nevertheless, we try to +write everything in English in the future. + +## Contribution + +It is an open source project, which is to be better and better. If you have any +ideas or find an error, or get stuck, you can contact us, please file a bug +report or send a pull-request! + +## License + +[_GPL2_](https://www.gnu.org/licenses/gpl-2.0.html) + +(C) Copyright 2010-2014 Andras Jeszenszky, [JSS & Hayer +IT](http://www.jsshayer.hu) All Rights Reserved. diff --git a/ami_test_client/libc-jss/amysql/Makefile b/ami_test_client/libc-jss/amysql/Makefile new file mode 100644 index 0000000000..4b4b38f22c --- /dev/null +++ b/ami_test_client/libc-jss/amysql/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -Wall -ggdb -I.. +CFLAGS += -DCON_DEBUG + +LIBS = -lev -lm -lmysqlclient -lpthread +OBJ=amysql.o ../logger.o ../misc.o + +all: test_amysql test_sync_amysql + +test_amysql: test_amysql.o $(OBJ) + $(CC) $(CFLAGS) -o test_amysql test_amysql.o $(OBJ) $(LIBS) + +test_sync_amysql: test_sync_amysql.o $(OBJ) + $(CC) $(CFLAGS) -o test_sync_amysql test_sync_amysql.o $(OBJ) $(LIBS) + +clean: + rm -f $(OBJ) *.o test_amysql test_sync_amysql + + diff --git a/ami_test_client/libc-jss/amysql/NOTES.txt b/ami_test_client/libc-jss/amysql/NOTES.txt new file mode 100644 index 0000000000..82b883c7d8 --- /dev/null +++ b/ami_test_client/libc-jss/amysql/NOTES.txt @@ -0,0 +1,30 @@ +libzdb - http://www.tildeslash.com/libzdb/ +A small, easy to use Open Source Database Connection Pool Library with the +following features: +-Thread safe Database Connection Pool +-Connect to multiple database systems +-Zero runtime configuration, connect using a URL scheme +-Supports MySQL, PostgreSQL, SQLite and Oracle + +MySQL C Library API Reference: +http://dev.mysql.com/doc/refman/5.1/en/c-api-functions.html + +MySQL C API Tutorial: +http://zetcode.com/tutorials/mysqlcapitutorial/ + +MySQL szerverben trigger - adding a New User-Defined Function +http://dev.mysql.com/doc/refman/5.5/en/adding-udf.html + + + +http://jan.kneschke.de/2008/9/9/async-mysql-queries-with-c-api/ + +tömör leírás az async elvről: +http://betterlogic.com/roger/2008/08/gdb-says-asynchronous-mysql-c-api/ + +http://stackoverflow.com/questions/11313686/using-libmysqlclient-in-multi-threaded-application + +http://www.linuxtopia.org/online_books/database_guides/mysql_5.1_database_reference_guide/threaded-clients.html + +http://dev.mysql.com/doc/refman/5.1/en/threaded-clients.html + diff --git a/ami_test_client/libc-jss/amysql/amysql.c b/ami_test_client/libc-jss/amysql/amysql.c new file mode 100644 index 0000000000..0d04fe2f6c --- /dev/null +++ b/ami_test_client/libc-jss/amysql/amysql.c @@ -0,0 +1,321 @@ +/* amysql.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* TODO: megvizsgálni a libzdb-t, mert ismeri a MySQL-t, PostgreSQL-t és az +Oracle-t is. A libzdb tudtommal nem aszinkron, viszont az amysql.c mintájára +aszinkronná lehetne tenni:) */ + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "logger.h" + +#define FIELDBUFSIZE 8192 +#define FIELDVSIZE 32 +#define QUERYBUFSIZE 4096 + +MYSQL *db; +MYSQL_RES *result; +MYSQL_ROW row; +int busy = 0; +void (*callback)(int, char**, char*); + +int fieldc; // result mezők száma +char *fieldv[FIELDVSIZE]; // result mező elemek +char fieldbuf[FIELDBUFSIZE]; // result buffer (ide mutatnak a fieldv elemek) +char querybuf[QUERYBUFSIZE]; // itt tároljuk el a query-t +char error_str[256]; +int error_num; + +struct option_t { + char host[64]; + char user[64]; + char password[64]; + char database[64]; + char charset[16]; + int connect_timeout; + int data_timeout; +} option = { + .host = "localhost", + .user = "", + .password = "", + .database = "", + .charset = "", // alapértelmezetten nem piszkáljuk a charset-et + .connect_timeout = 10, // TODO: prod környezetben ezeket magasra venni! + .data_timeout = 20, /* Each attempt uses this timeout value and there + are retries if necessary, so the total effective + timeout value is three times the option value. */ +}; + +static ev_async evasync_main; +static ev_async evasync_thread; + +static struct ev_loop *loop_main; // főprogram event loopja (kapja az amysql_init()) +static struct ev_loop *loop_thread; // thread event loopja + +pthread_t thread_id; + +void amysql_option_host (const char *host) { + strncpy(option.host, host, sizeof(option.host) - 1); +} + +void amysql_option_user (const char *user) { + strncpy(option.user, user, sizeof(option.user) - 1); +} + +void amysql_option_password (const char *password) { + strncpy(option.password, password, sizeof(option.password) - 1); +} + +void amysql_option_database (const char *database) { + strncpy(option.database, database, sizeof(option.database) - 1); +} + +void amysql_option_charset (const char *charset) { + strncpy(option.charset, charset, sizeof(option.charset) - 1); +} + +void amysql_option_connect_timeout (int connect_timeout) { + option.connect_timeout = connect_timeout; +} + +void amysql_option_data_timeout (int data_timeout) { + option.data_timeout = data_timeout; +} + +// thread: AMYSQL, ha amysql_query() hívás volt +// thread: nincs definiálva, ha amysql_sync_query() hívás volt +// Függvény a következőket csinálja: +// reset, init, config, connect, query, fetch, save, disconnect, free +static void query () { + con_debug("send query: %s", querybuf); + //** Buffer reset **// + memset(fieldbuf, 0, sizeof(fieldbuf)); + memset(fieldv, 0, sizeof(fieldv)); + memset(error_str, 0, sizeof(error_str)); + fieldc = 0; + db = NULL; + result = NULL; + + //** Init **// + db = mysql_init(NULL); + if (db == NULL) { + con_debug("mysql_init() returned NULL"); + char *errmsg = "mysql_init(): Can't allocate memory"; + strncpy(error_str, errmsg, sizeof(error_str)); + error_num = -1; + goto done; + } + + //** Config **// + mysql_options(db, MYSQL_OPT_CONNECT_TIMEOUT, &option.connect_timeout); + mysql_options(db, MYSQL_OPT_READ_TIMEOUT, &option.data_timeout); + mysql_options(db, MYSQL_OPT_WRITE_TIMEOUT, &option.data_timeout); + + //** Connect **// + if (!mysql_real_connect(db, option.host, option.user, option.password, option.database, 0, NULL, 0)) { + con_debug("Can't connect to MySQL server: %s", mysql_error(db)); + strncpy(error_str, mysql_error(db), sizeof(error_str)); + error_num = -1; + goto done; + } + + if (option.charset && strlen(option.charset)) { + if (mysql_set_character_set(db, option.charset)) { + strncpy(error_str, mysql_error(db), sizeof(error_str)); + error_num = -1; + goto done; + } + } + + //** Query **// + if (mysql_query(db, querybuf)) {; + con_debug("query failed: %s", mysql_error(db)); + strncpy(error_str, mysql_error(db), sizeof(error_str)); + error_num = -1; + goto done; + } + + //** Result **// + result = mysql_store_result(db); + if (result == NULL) { // INSERT INTO sikeres + con_debug("INSERT or UPDATE OK: result = NULL"); + error_num = 0; + goto done; + } + + //** Fetch only ONE row **// + int num_fields = mysql_num_fields(result); + row = mysql_fetch_row(result); + if (row == NULL) { // SELECT-ben a WHERE nem illeszkedett semmire + con_debug("SELECT empty: row = NULL"); + error_num = 0; + goto done; + } + + /* Az alábbi for ciklus a következőket csinálja: Egyetlen row mezőinek + másolása fieldbuf bufferbe és feldarabolása fieldv-be. A fieldbuf-ba + folyamatosan bemásoljuk a row-ból kiolvasott byteokat. Ezzel a + folytonossággal helyet spórolunk meg, mert nem kell minden mezőnek fix + méretet lefoglalni. A fieldv mutató tömbbe pedig az egyes mezők + kezdőcimeit mentjük el, ami a fieldbuf azon indexére mutat, ahol az + adott mező kezdődik. Az argc és *argv[] pároshoz hasonlóan a fieldc + változóba kerül a fieldv tömb mérete. Ha elegendő volt a FIELDBUFSIZE és + a FIELDVSIZE, akkor ez megegyezik a MySQL által visszaadott + mysql_num_fields() értékével. Ellenkező esetben annyi lesz, amennyit + sikerült kigyűjteni, tehát amennyit biztonsággal ki lehet olvasni. */ + + //** Save **// + int rpos = 0; + for (fieldc = 0; fieldc < num_fields && fieldc < FIELDVSIZE; fieldc++) { + if (row[fieldc] == NULL) { + fieldv[fieldc] = NULL; + } else { + fieldv[fieldc] = &fieldbuf[rpos]; + int i; + for (i = 0; row[fieldc][i] != 0; i++) { + fieldbuf[rpos] = row[fieldc][i]; + rpos++; + if (rpos > FIELDBUFSIZE - 2) { // ne szaladjunk túl a fieldbuf méretén + fieldc++; + error_num = 0; + goto done; + } + } + fieldbuf[rpos] = 0; + rpos++; + if (rpos > FIELDBUFSIZE - 2) { // ne szaladjunk túl a fieldbuf méretén + fieldc++; + error_num = 0; + goto done; + } + } + } + //~ int k; for (k = 0; k < fieldc; k++) printf("%d s %s s\n", k, fieldv[k]); printf("\n"); + +done: + //** Free & Disconnect **// + if (result != NULL) + mysql_free_result(result); + if (db != NULL) + mysql_close(db); + + // TODO: strerror üzenetét hozzácsapni az errstr-hez, az err != 0 + // ha 4-es hiba (interrupted system call) jön, akkor azt a mysql_connect() + // timeout okozza. Vajon ez hogy van megoldva? SIGALRM??? Az bebaszna! + //perror("strerror()"); + +} + +void query_and_evasync () { + query(); + + con_debug("ev_async: AMYSQL -> MAIN"); + ev_async_send(loop_main, &evasync_main); +} + +// thread: MAIN +int amysql_is_busy () { + return busy; +} + +// thread: MAIN +int amysql_query (void *cb, const char *fmt, ...) { + if (busy) { + con_debug("amysql is BUSY"); + return -1; + } + busy = 1; + + va_list ap; + va_start(ap, fmt); + vsnprintf(querybuf, sizeof(querybuf) - 1, fmt, ap); + va_end(ap); + + callback = cb; + + con_debug("ev_async: MAIN -> AMYSQL"); + ev_async_send(loop_thread, &evasync_thread); + return 0; +} + +// thread: MAIN +static void invoke_callback (EV_P_ ev_async *w, int revents) { + /* callback hívás közben már lehet új amysql_query() függvényt hívni, + mert féligmeddig biztonságos. Lehetőség szerint a callback függvény + legvégén legyen új amysql_query() hívás, mert az amysql thread felül + fogja írni a fieldc/fieldv és error_num/error_str változókat. */ + busy = 0; + + callback(fieldc, fieldv, (error_num) ? error_str : NULL); +} + +// thread: AMYSQL +static void *infinite_loop () { + /* Itt lehet inicializálni az AMYSQL thread saját event watchereit */ + + ev_loop(loop_thread, 0); + con_debug("FATAL: amysql event loop exited! "); + return NULL; +} + +// thread: MAIN +int amysql_init (struct ev_loop *loop) { + busy = 0; + + loop_main = loop; + loop_thread = ev_loop_new(EVFLAG_AUTO); + + ev_async_init(&evasync_main, (void*)invoke_callback); + ev_async_init(&evasync_thread, (void*)query_and_evasync); + + /* A mostani kezdetleges thread tapasztalataim alapján, szerintem az + ev_async watchereket itt, még a pthread_create() előtt kell indítani. + Azaz még a hívó thread kontextusában. Mert ha az új thread-be + indítanánk, akkor elképzelhető, hogy visszatérés után a hívó függvény + egyből egy ev_async_send() hívással folytatja a futást és lehet, hogy az + új thread még nem járna az ev_async_init()-nél és ev_async_start()-nál. + Ez pedig ugye nem jó, mert azelőtt hívnánk meg valamit, mint hogy + inicializáltuk volna. A többi, (a thread-re nézve saját) event watcher, + io, időzítő, stb.. inicializálását már végezheti a thread saját maga, + tehát mehet a pthread_create() által meghívott függvénybe. */ + ev_async_start(loop_main, &evasync_main); + ev_async_start(loop_thread, &evasync_thread); + + int err; + err = pthread_create(&thread_id, NULL, infinite_loop, NULL); + if (err) + con_debug("thread creating error: (%d) %s", err, strerror(err)); + return err; +} + +int amysql_sync_query (int *parc, char **parv[], const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(querybuf, sizeof(querybuf) - 1, fmt, ap); + va_end(ap); + + query(); + + if (parc) + *parc = fieldc; + if (parv) + *parv = fieldv; + + return error_num; +} + +const char *amysql_strerror () { + return (error_num) ? error_str : NULL; +} diff --git a/ami_test_client/libc-jss/amysql/amysql.h b/ami_test_client/libc-jss/amysql/amysql.h new file mode 100644 index 0000000000..a26036cafc --- /dev/null +++ b/ami_test_client/libc-jss/amysql/amysql.h @@ -0,0 +1,55 @@ +/* amysql.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include + +/* Azért kell a loop paraméter, mert innen tudja az amysql, hogy melyik +event loop-ban kell majd meghívni a callback-et.*/ + +int amysql_init (struct ev_loop *loop); + +/* Az amysql_query() azonnal visszaadja a vezérlést, és a háttérben egy +másik szálon megkezdi a paraméterben megadott lekérdezést az adatbázis +szerver felé. Amikor megjön a válasz, akkor lezárja a szerverrel a +kapcsolatot és meghívja a paraméterben megadott callback függvényt. + +Figyelem! A callback függvény végén, de tényleg a legvégén lehetőség van új +amysql_query() függvény hívására. Hatására a fieldc/fieldv és a hibaváltozók +törlődni fognak, amiket a callback függvény is kapott.*/ + +int amysql_query (void *cb, const char *fmt, ...); + + +/* Konfigurációs függvények */ + +// default: localhost +void amysql_option_host (const char *host); + +void amysql_option_user (const char *user); + +void amysql_option_password (const char *password); + +void amysql_option_database (const char *database); + +// default: nem hívja meg a mysql_set_character_set() függvényt +void amysql_option_charset (const char *charset); + +// default: 10 +void amysql_option_connect_timeout (int connect_timeout); + +// default: 20 +void amysql_option_data_timeout (int data_timeout); + + +// visszaadja a hiba szövegét vagy NULL-t, ha nem volt hiba +const char *amysql_strerror (); + +// nem-aszinkron lekérdezés. A lekérdezés erejéig az amysql_sync_query() hívás +// blokkol. Az eredményt a parc és a parv-be menti el. +int amysql_sync_query (int *parc, char **parv[], const char *fmt, ...); + diff --git a/ami_test_client/libc-jss/amysql/test_amysql.c b/ami_test_client/libc-jss/amysql/test_amysql.c new file mode 100644 index 0000000000..84bac62de8 --- /dev/null +++ b/ami_test_client/libc-jss/amysql/test_amysql.c @@ -0,0 +1,57 @@ +/* test_amysql.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "amysql.h" +#include "debug.h" + +static struct ev_loop *loop; +static ev_timer timeout_watcher; + +static void cbtest (int fc, char *fv[], char *e); + +static void q () { + amysql_query(cbtest, "select * from smsout where sent='0' order by RAND()"); +} + +static void cbtest (int fc, char *fv[], char *e) { + debi(fc); + printf("e=%s\n", e); + int i; + for (i = 0; i < fc; i++) { + printf("%d --- %s\n", i, fv[i]); + } + + // 3 másodperc múlva q() futtatása + ev_once(loop, -1, -1, 3, q, 0); +} + +static void timeout_cb (EV_P_ ev_timer *w, int revents) { + printf("timeout\n"); +} + +int main (int argc, char *argv[]) { + loop = ev_default_loop (0); + + ev_timer_init (&timeout_watcher, timeout_cb, 0, 0.5); + ev_timer_start (loop, &timeout_watcher); + + amysql_init(loop); + + q(); // berúgjuk a mocit:) + + ev_loop (loop, 0); + + return 0; +} + diff --git a/ami_test_client/libc-jss/amysql/test_sync_amysql.c b/ami_test_client/libc-jss/amysql/test_sync_amysql.c new file mode 100644 index 0000000000..1d8db7d5f8 --- /dev/null +++ b/ami_test_client/libc-jss/amysql/test_sync_amysql.c @@ -0,0 +1,40 @@ +/* test_sync_amysql.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include + +#include "amysql.h" +#include "debug.h" + +int main (int argc, char *argv[]) { + amysql_option_user ("proba"); + amysql_option_password ("proba"); + amysql_option_database ("proba"); + amysql_option_charset ("utf8"); + + // if (amysql_sync_query(NULL, NULL, "INSERT INTO proba (name, comment) VALUES ('%s', '%s')", "egy", "kettő")) { + // printf("ERROR: %s\n", amysql_strerror()); + // } + + int parc; + char **parv; + if (amysql_sync_query(&parc, &parv, "select * from proba order by RAND()")) { + printf("ERROR: %s\n", amysql_strerror()); + } + + int i; + for (i = 0; i < parc; i++) { + printf("%d --- %s\n", i, parv[i]); + } + + return 0; +} + diff --git a/ami_test_client/libc-jss/debug.h b/ami_test_client/libc-jss/debug.h new file mode 100644 index 0000000000..39e94b1ec7 --- /dev/null +++ b/ami_test_client/libc-jss/debug.h @@ -0,0 +1,12 @@ +/* debug.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#define debi(x) printf(#x " = %d\n", ((int)(x))) +#define debf(x) printf(#x " = %f\n", (x)) +#define debs(x) printf(#x " = %s\n", (x)) + diff --git a/ami_test_client/libc-jss/fmtsub_test.c b/ami_test_client/libc-jss/fmtsub_test.c new file mode 100644 index 0000000000..04ff6b2a31 --- /dev/null +++ b/ami_test_client/libc-jss/fmtsub_test.c @@ -0,0 +1,62 @@ +/* fmtsub_test.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include + +#include "misc.h" + +/* +const char *args[][2] = { + {"%n", "2463434"}, + {"%v", "0.9.6-rc2"}, + {"%n", "Jekusa Poromeck"}, + {0} +}; +*/ +//~ void prob (const char *args[][2]) { + //~ int i; + //~ for (i = 0; afmt[i][0] != 0; i++) { + //~ printf("hello %s --- %s\n", afmt[i][0], afmt[i][1]); + //~ + //~ } +//~ } + +int main (int argc, char **argv) { + char buf2[32]; + char buf[64]; + + char *pattern = argv[1]; + + char proba[64]; + strcpy(proba, "PROBA"); + + const char *args[][2] = { + {"%n", NULL}, + {"", "URES_STRING"}, + {"%v", "0.9.6-rc2"}, + {"%a", "Jekusa Poromeck"}, + {"%p", proba}, + {"Windows", "Linux"}, + {"\\%", "%"}, + {0} + }; + + if (fmtsub(buf, sizeof(buf), pattern, args)) { + printf("fmtsub() hibaval tert vissza!\n"); + } + + printf("%d s %s s\n", (int)strlen(buf), buf); + + strncpy(buf2, buf, sizeof(buf2)-1); + printf("%d s %s s\n", (int)strlen(buf2), buf2); + + return 0; +} diff --git a/ami_test_client/libc-jss/htclient/Makefile b/ami_test_client/libc-jss/htclient/Makefile new file mode 100644 index 0000000000..5735fdda19 --- /dev/null +++ b/ami_test_client/libc-jss/htclient/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wall -I.. -ggdb +LDFLAGS = -lpthread + +all: test_htclient + +test_htclient: test_htclient.o htclient.o url_parser.o + $(CC) $(CFLAGS) -o test_htclient test_htclient.o htclient.o url_parser.o $(LDFLAGS) + +clean: + rm -f test_htclient *.o diff --git a/ami_test_client/libc-jss/htclient/htclient.c b/ami_test_client/libc-jss/htclient/htclient.c new file mode 100644 index 0000000000..c3e85e4958 --- /dev/null +++ b/ami_test_client/libc-jss/htclient/htclient.c @@ -0,0 +1,235 @@ +/* htclient.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "htclient.h" +#include "debug.h" +#include "url_parser.h" + +/* void ERR(...) + * + * A paraméterül átadott VA_ARGS formátumú hibaüzenetet bemásolja a req->error + * -ba, majd visszatér return -1 -el. Figyelni kell a blokkos szerkezeteknél: + * Mindenképp használni kell a { } zárójeleket, mivel ez a makró két utasítást + * tartalmaz. A { } elhagyásával logikai hiba keletkezhet a programban. + */ +#define ERR(...) snprintf(htc->error, sizeof(htc->error), __VA_ARGS__); return -1 + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +void htclient_dump (htclient_t *htc) { + #define HTCLIENT_DUMPS(s) printf("%13s = %s\n", #s, htc->s) + #define HTCLIENT_DUMPI(i) printf("%13s = %d\n", #i, htc->i) + HTCLIENT_DUMPS(url.full); + HTCLIENT_DUMPS(url.scheme); + HTCLIENT_DUMPS(url.host); + HTCLIENT_DUMPS(url.port); + HTCLIENT_DUMPS(url.path); + HTCLIENT_DUMPS(url.query); + HTCLIENT_DUMPS(url.fragment); + HTCLIENT_DUMPS(url.username); + HTCLIENT_DUMPS(url.password); + HTCLIENT_DUMPS(request_data); + HTCLIENT_DUMPS(response_data); + HTCLIENT_DUMPI(num_headers); + HTCLIENT_DUMPS(header_buf); + HTCLIENT_DUMPS(error); +} + +// Skip the characters until one of the delimiters characters found. +// 0-terminate resulting word. Skip the rest of the delimiters if any. Advance +// pointer to buffer to the next word. Return found 0-terminated word. +// (function from Mongoose project) +static char *skip (char **buf, const char *delimiters) { + char *p, *begin_word, *end_word, *end_delimiters; + + begin_word = *buf; + end_word = begin_word + strcspn(begin_word, delimiters); + end_delimiters = end_word + strspn(end_word, delimiters); + + for (p = end_word; p < end_delimiters; p++) { + *p = '\0'; + } + + *buf = end_delimiters; + + return begin_word; +} + +// Parse HTTP headers from the given buffer, advance buffer to the point where +// parsing stopped. (function from Mongoose project) +static void parse_http_headers(char **buf, struct htclient_t *htc) { + size_t i; + + for (i = 0; i < ARRAY_SIZE(htc->headers); i++) { + htc->headers[i].name = skip(buf, ": "); + htc->headers[i].value = skip(buf, "\r\n"); + if (htc->headers[i].name[0] == '\0') + break; + htc->num_headers = i + 1; + } +} + +char *htclient_error (htclient_t *htc) { + if (htc->error == NULL || !strlen(htc->error)) + return NULL; + return htc->error; +} + +int htclient_url (htclient_t *htc, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(htc->url.full, sizeof(htc->url.full), fmt, ap); + va_end(ap); + + struct parsed_url *pu = parse_url(htc->url.full); + if (pu == NULL) { + htc->url.set = 0; + ERR("URL mismatch"); + } + + htc->url.url_parser_ptr = pu; + htc->url.scheme = pu->scheme; + htc->url.host = pu->host; + htc->url.port = pu->port; + htc->url.path = pu->path; + htc->url.query = pu->query; + htc->url.fragment = pu->fragment; + htc->url.username = pu->username; + htc->url.password = pu->password; + + // ha nincs megadva a port, akkor a scheme-ből megállapítjuk + if (!htc->url.port && htc->url.scheme) { + if (!strcmp(htc->url.scheme, "http")) { + // TODO: az strdup() nem okoz memória szivárgást? Ha felszabadítjuk + // az url_parser-t, akkor az felszabadítja ezt is? + htc->url.port = strdup("80"); + } else if (!strcmp(htc->url.scheme, "https")) { + htc->url.port = strdup("443"); + } + } + + // ha valamilyen oknál fogva ezen a ponton nincs + // host vagy nincs port, akkor a további + // grimbuszok elkerülése végett hivát dobunk:) + if (!htc->url.host || !htc->url.port) { + htc->url.set = 0; + ERR("URL mismatch"); + } + + htc->url.set = 1; + return 0; +} + +int htclient_perform (htclient_t *htc) { + struct sockaddr_in serv_addr; + struct hostent *server; + int port; + int sockfd; + int rv; + + if (!htc->url.set) { + ERR("URL not set or bogus"); + } + + port = atoi(htc->url.port); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + ERR("error opening socket: %s", strerror(errno)); + } + + server = gethostbyname(htc->url.host); + if (server == NULL) { + close(sockfd); + ERR("host not found: %s", htc->url.host); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, + (char *)&serv_addr.sin_addr.s_addr, + server->h_length); + serv_addr.sin_port = htons(port); + if (connect(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { + close(sockfd); + ERR("can't connect to %s:%d: %s", htc->url.host, port, strerror(errno)); + } + + rv = write(sockfd, htc->request_data, strlen(htc->request_data)); + if (rv < 0) { + close(sockfd); + ERR("ERROR writing to socket"); + } + + memset(htc->response_data, 0, sizeof(htc->response_data)); + rv = read(sockfd, htc->response_data, sizeof(htc->response_data) - 1); + if (rv < 0) { + close(sockfd); + ERR("ERROR reading from socket"); + } + + htc->header_buf = strdup(htc->response_data); + char *buf = htc->header_buf; + + parse_http_headers(&buf, htc); + close(sockfd); + + return 0; +} + +char *htclient_header_get (htclient_t *htc, const char *name) { + int i; + for (i = 0; i < htc->num_headers; i++) { + if (htc->headers[i].name == NULL || htc->headers[i].value == NULL) + continue; + if (!strcmp(htc->headers[i].name, name)) + return htc->headers[i].value; + } + return NULL; +} + + +htclient_t *htclient_new () { + htclient_t *htc = malloc(sizeof(*htc)); + if (htc == NULL) { + fprintf(stderr, "out of memory"); + return NULL; + } + memset(htc, 0, sizeof(*htc)); + return htc; +} + +void htclient_request_set (htclient_t *htc, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(htc->request_data, sizeof(htc->request_data), fmt, ap); + va_end(ap); +} + + +void htclient_destroy (htclient_t *htc) { + if (htc == NULL) + return; + if (htc->header_buf != NULL) { + free(htc->header_buf); + htc->header_buf = NULL; + } + free(htc); +} + diff --git a/ami_test_client/libc-jss/htclient/htclient.h b/ami_test_client/libc-jss/htclient/htclient.h new file mode 100644 index 0000000000..0708d38604 --- /dev/null +++ b/ami_test_client/libc-jss/htclient/htclient.h @@ -0,0 +1,67 @@ +/* htclient.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#define HTCLIENT_BUFSIZ 1024 + +// változás esetén módosítsd a htclient_dump() függvényt a htclient.c fájlban! +typedef struct htclient_t { + struct { + void *url_parser_ptr; // url_parser.h -ban definiált pointer, ezt kell majd felszabadítani + char full[HTCLIENT_BUFSIZ]; // http://jsi@jss.hu/auth?param=59 + char *scheme; // http + char *host; // jss.hu + char *port; // (null) + char *path; // auth + char *query; // param=59 + char *fragment; // (null) + char *username; // jsi + char *password; // (null) + int set; // 1 lesz, ha rendben beállítottuk az URL-t + } url; + char request_data[HTCLIENT_BUFSIZ]; + char response_data[HTCLIENT_BUFSIZ]; + struct { + char *name; + char *value; + } headers[16]; + int num_headers; + char *header_buf; + char error[128]; +} htclient_t; + +// új htclient objektum +htclient_t *htclient_new (); + +// URL beállítása +// Kezeli a scheme, host, port, path URL részeket. Tovább infó a +// htclient_t->url struktúrában. Az URL path részét nem kezeli a +// htclient_perform(), ezért azt külön el kell menteni a request-ben. +// +// például: http://jss.hu:3333/akarmi.jpg +int htclient_url (htclient_t *htc, const char *fmt, ...); + +// Beállítja a teljes HTTP request-et. Beleértve a fejléceket meg mindent. +void htclient_request_set (htclient_t *htc, const char *fmt, ...); + +// Végrehajtja a lekérdezést. A függvény addig blokkol, amíg nem érkezik meg a +// válasz vagy valami hiba történik. +int htclient_perform (htclient_t *htc); + +// Visszaadja a válaszban szereplő header értékét. Ha a megadott header nem +// létezik, akkor a visszatérési érték NULL. +char *htclient_header_get (htclient_t *htc, const char *name); + +// Felszabadítja a htclient objektumot +void htclient_destroy (htclient_t *htc); + +// Ha volt hiba, akkor visszaadja a hibastringet, ha nem, akkor NULL +char *htclient_error (htclient_t *htc); + +// htclient_t struktúra dumpolása +void htclient_dump (htclient_t *htc); + diff --git a/ami_test_client/libc-jss/htclient/test_htclient.c b/ami_test_client/libc-jss/htclient/test_htclient.c new file mode 100644 index 0000000000..23d969e2ba --- /dev/null +++ b/ami_test_client/libc-jss/htclient/test_htclient.c @@ -0,0 +1,66 @@ +/* test_htclient.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "htclient.h" +#include "debug.h" +#include "url_parser.h" + +void url_test () { + htclient_t *h = htclient_new(); + + htclient_url(h, "http://10.42.20.14:3000/akarmi.jpg"); + htclient_dump(h); + + htclient_destroy(h); +} + +int main (int argc, const char *argv[]) { + // url_test(); return(0); + + htclient_t *h; + h = htclient_new(); + if (h == NULL) { + printf("out of memory\n"); + return -1; + } + + htclient_url(h, "http://10.42.20.14:3000/dovecheck"); + htclient_request_set(h, + "GET /auth\r\n" + "Auth-server: akarmi\r\n" + "Auth-port: %d\r\n" + "\r\n" + , 5555 + ); + htclient_perform(h); + +htclient_dump(h); + if (htclient_error(h)) { + printf("error: %s\n", htclient_error(h)); + goto err; + } + + char *hdr = htclient_header_get(h, "Auth-server"); + printf("Header vissza: %s\n", hdr); + + printf("len = %s\n", htclient_header_get(h, "Content-Length")); + + htclient_destroy(h); + return 0; + +err: + htclient_destroy(h); + return -1; +} + diff --git a/ami_test_client/libc-jss/htclient/url_parser.c b/ami_test_client/libc-jss/htclient/url_parser.c new file mode 100644 index 0000000000..6c542d562f --- /dev/null +++ b/ami_test_client/libc-jss/htclient/url_parser.c @@ -0,0 +1,329 @@ +/*_ + * Copyright 2010-2011 Scyphus Solutions Co. Ltd. All rights reserved. + * + * Authors: + * Hirochika Asai + */ + +#include "url_parser.h" + +#include +#include +#include +#include + +/* + * Prototype declarations + */ +static __inline__ int _is_scheme_char(int); + +/* + * Check whether the character is permitted in scheme string + */ +static __inline__ int +_is_scheme_char(int c) +{ + return (!isalpha(c) && '+' != c && '-' != c && '.' != c) ? 0 : 1; +} + +/* + * See RFC 1738, 3986 + */ +struct parsed_url * +parse_url(const char *url) +{ + struct parsed_url *purl; + const char *tmpstr; + const char *curstr; + int len; + int i; + int userpass_flag; + int bracket_flag; + + /* Allocate the parsed url storage */ + purl = malloc(sizeof(struct parsed_url)); + if ( NULL == purl ) { + return NULL; + } + purl->scheme = NULL; + purl->host = NULL; + purl->port = NULL; + purl->path = NULL; + purl->query = NULL; + purl->fragment = NULL; + purl->username = NULL; + purl->password = NULL; + + curstr = url; + + /* + * : + * := [a-z\+\-\.]+ + * upper case = lower case for resiliency + */ + /* Read scheme */ + tmpstr = strchr(curstr, ':'); + if ( NULL == tmpstr ) { + /* Not found the character */ + parsed_url_free(purl); + return NULL; + } + /* Get the scheme length */ + len = tmpstr - curstr; + /* Check restrictions */ + for ( i = 0; i < len; i++ ) { + if ( !_is_scheme_char(curstr[i]) ) { + /* Invalid format */ + parsed_url_free(purl); + return NULL; + } + } + /* Copy the scheme to the storage */ + purl->scheme = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->scheme ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->scheme, curstr, len); + purl->scheme[len] = '\0'; + /* Make the character to lower if it is upper case. */ + for ( i = 0; i < len; i++ ) { + purl->scheme[i] = tolower(purl->scheme[i]); + } + /* Skip ':' */ + tmpstr++; + curstr = tmpstr; + + /* + * //:@:/ + * Any ":", "@" and "/" must be encoded. + */ + /* Eat "//" */ + for ( i = 0; i < 2; i++ ) { + if ( '/' != *curstr ) { + parsed_url_free(purl); + return NULL; + } + curstr++; + } + + /* Check if the user (and password) are specified. */ + userpass_flag = 0; + tmpstr = curstr; + while ( '\0' != *tmpstr ) { + if ( '@' == *tmpstr ) { + /* Username and password are specified */ + userpass_flag = 1; + break; + } else if ( '/' == *tmpstr ) { + /* End of : specification */ + userpass_flag = 0; + break; + } + tmpstr++; + } + + /* User and password specification */ + tmpstr = curstr; + if ( userpass_flag ) { + /* Read username */ + while ( '\0' != *tmpstr && ':' != *tmpstr && '@' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->username = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->username ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->username, curstr, len); + purl->username[len] = '\0'; + /* Proceed current pointer */ + curstr = tmpstr; + if ( ':' == *curstr ) { + /* Skip ':' */ + curstr++; + /* Read password */ + tmpstr = curstr; + while ( '\0' != *tmpstr && '@' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->password = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->password ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->password, curstr, len); + purl->password[len] = '\0'; + curstr = tmpstr; + } + /* Skip '@' */ + if ( '@' != *curstr ) { + parsed_url_free(purl); + return NULL; + } + curstr++; + } + + if ( '[' == *curstr ) { + bracket_flag = 1; + } else { + bracket_flag = 0; + } + /* Proceed on by delimiters with reading host */ + tmpstr = curstr; + while ( '\0' != *tmpstr ) { + if ( bracket_flag && ']' == *tmpstr ) { + /* End of IPv6 address. */ + tmpstr++; + break; + } else if ( !bracket_flag && (':' == *tmpstr || '/' == *tmpstr) ) { + /* Port number is specified. */ + break; + } + tmpstr++; + } + len = tmpstr - curstr; + purl->host = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->host || len <= 0 ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->host, curstr, len); + purl->host[len] = '\0'; + curstr = tmpstr; + + /* Is port number specified? */ + if ( ':' == *curstr ) { + curstr++; + /* Read port number */ + tmpstr = curstr; + while ( '\0' != *tmpstr && '/' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->port = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->port ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->port, curstr, len); + purl->port[len] = '\0'; + curstr = tmpstr; + } + + /* End of the string */ + if ( '\0' == *curstr ) { + return purl; + } + + /* Skip '/' */ + if ( '/' != *curstr ) { + parsed_url_free(purl); + return NULL; + } + curstr++; + + /* Parse path */ + tmpstr = curstr; + while ( '\0' != *tmpstr && '#' != *tmpstr && '?' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->path = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->path ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->path, curstr, len); + purl->path[len] = '\0'; + curstr = tmpstr; + + /* Is query specified? */ + if ( '?' == *curstr ) { + /* Skip '?' */ + curstr++; + /* Read query */ + tmpstr = curstr; + while ( '\0' != *tmpstr && '#' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->query = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->query ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->query, curstr, len); + purl->query[len] = '\0'; + curstr = tmpstr; + } + + /* Is fragment specified? */ + if ( '#' == *curstr ) { + /* Skip '#' */ + curstr++; + /* Read fragment */ + tmpstr = curstr; + while ( '\0' != *tmpstr ) { + tmpstr++; + } + len = tmpstr - curstr; + purl->fragment = malloc(sizeof(char) * (len + 1)); + if ( NULL == purl->fragment ) { + parsed_url_free(purl); + return NULL; + } + (void)strncpy(purl->fragment, curstr, len); + purl->fragment[len] = '\0'; + curstr = tmpstr; + } + + return purl; +} + +/* + * Free memory of parsed url + */ +void +parsed_url_free(struct parsed_url *purl) +{ + if ( NULL != purl ) { + if ( NULL != purl->scheme ) { + free(purl->scheme); + } + if ( NULL != purl->host ) { + free(purl->host); + } + if ( NULL != purl->port ) { + free(purl->port); + } + if ( NULL != purl->path ) { + free(purl->path); + } + if ( NULL != purl->query ) { + free(purl->query); + } + if ( NULL != purl->fragment ) { + free(purl->fragment); + } + if ( NULL != purl->username ) { + free(purl->username); + } + if ( NULL != purl->password ) { + free(purl->password); + } + free(purl); + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + diff --git a/ami_test_client/libc-jss/htclient/url_parser.h b/ami_test_client/libc-jss/htclient/url_parser.h new file mode 100644 index 0000000000..94f8d61ed5 --- /dev/null +++ b/ami_test_client/libc-jss/htclient/url_parser.h @@ -0,0 +1,49 @@ +/*_ + * Copyright 2010 Scyphus Solutions Co. Ltd. All rights reserved. + * + * Authors: + * Hirochika Asai + */ + +#ifndef _URL_PARSER_H +#define _URL_PARSER_H + +/* + * URL storage + */ +struct parsed_url { + char *scheme; /* mandatory */ + char *host; /* mandatory */ + char *port; /* optional */ + char *path; /* optional */ + char *query; /* optional */ + char *fragment; /* optional */ + char *username; /* optional */ + char *password; /* optional */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Declaration of function prototypes + */ + struct parsed_url *parse_url (const char *); + void parsed_url_free (struct parsed_url *); + +#ifdef __cplusplus +} +#endif + +#endif /* _URL_PARSER_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ + diff --git a/ami_test_client/libc-jss/ini/LICENSE.txt b/ami_test_client/libc-jss/ini/LICENSE.txt new file mode 100644 index 0000000000..1d31de2801 --- /dev/null +++ b/ami_test_client/libc-jss/ini/LICENSE.txt @@ -0,0 +1,27 @@ + +The "inih" library is distributed under the New BSD license: + +Copyright (c) 2009, Brush Technology +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. + * Neither the name of Brush Technology nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''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 BRUSH TECHNOLOGY 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. diff --git a/ami_test_client/libc-jss/ini/Makefile b/ami_test_client/libc-jss/ini/Makefile new file mode 100644 index 0000000000..c9aa8d925a --- /dev/null +++ b/ami_test_client/libc-jss/ini/Makefile @@ -0,0 +1,9 @@ +CFLAGS = -Wall + +all: test_ini + +test_ini: ini.o test_ini.o + gcc -o test_ini $(CFLAGS) test_ini.o ini.o + +clean: + rm -f test_ini *.o diff --git a/ami_test_client/libc-jss/ini/README.txt b/ami_test_client/libc-jss/ini/README.txt new file mode 100644 index 0000000000..8e5f8b1d3e --- /dev/null +++ b/ami_test_client/libc-jss/ini/README.txt @@ -0,0 +1,5 @@ + +inih is a simple .INI file parser written in C, released under the New BSD +license (see LICENSE.txt). Go to the project home page for more info: + +http://code.google.com/p/inih/ diff --git a/ami_test_client/libc-jss/ini/cpp/INIReader.cpp b/ami_test_client/libc-jss/ini/cpp/INIReader.cpp new file mode 100644 index 0000000000..1181b8c600 --- /dev/null +++ b/ami_test_client/libc-jss/ini/cpp/INIReader.cpp @@ -0,0 +1,76 @@ +// Read an INI file into easy-to-access name/value pairs. + +#include +#include +#include +#include "../ini.h" +#include "INIReader.h" + +using std::string; + +INIReader::INIReader(string filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +int INIReader::ParseError() +{ + return _error; +} + +string INIReader::Get(string section, string name, string default_value) +{ + string key = MakeKey(section, name); + return _values.count(key) ? _values[key] : default_value; +} + +long INIReader::GetInteger(string section, string name, long default_value) +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +double INIReader::GetReal(string section, string name, double default_value) +{ + string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +bool INIReader::GetBoolean(string section, string name, bool default_value) +{ + string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +string INIReader::MakeKey(string section, string name) +{ + string key = section + "." + name; + // Convert to lower case to make section/name lookups case-insensitive + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + return key; +} + +int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + INIReader* reader = (INIReader*)user; + string key = MakeKey(section, name); + if (reader->_values[key].size() > 0) + reader->_values[key] += "\n"; + reader->_values[key] += value; + return 1; +} diff --git a/ami_test_client/libc-jss/ini/cpp/INIReader.h b/ami_test_client/libc-jss/ini/cpp/INIReader.h new file mode 100644 index 0000000000..e4d2207270 --- /dev/null +++ b/ami_test_client/libc-jss/ini/cpp/INIReader.h @@ -0,0 +1,53 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// http://code.google.com/p/inih/ + +#ifndef __INIREADER_H__ +#define __INIREADER_H__ + +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + INIReader(std::string filename); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError(); + + // Get a string value from INI file, returning default_value if not found. + std::string Get(std::string section, std::string name, + std::string default_value); + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(std::string section, std::string name, long default_value); + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(std::string section, std::string name, double default_value); + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(std::string section, std::string name, bool default_value); + +private: + int _error; + std::map _values; + static std::string MakeKey(std::string section, std::string name); + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // __INIREADER_H__ diff --git a/ami_test_client/libc-jss/ini/cpp/INIReaderTest.cpp b/ami_test_client/libc-jss/ini/cpp/INIReaderTest.cpp new file mode 100644 index 0000000000..29f07885a4 --- /dev/null +++ b/ami_test_client/libc-jss/ini/cpp/INIReaderTest.cpp @@ -0,0 +1,21 @@ +// Example that shows simple usage of the INIReader class + +#include +#include "INIReader.h" + +int main() +{ + INIReader reader("../examples/test.ini"); + + if (reader.ParseError() < 0) { + std::cout << "Can't load 'test.ini'\n"; + return 1; + } + std::cout << "Config loaded from 'test.ini': version=" + << reader.GetInteger("protocol", "version", -1) << ", name=" + << reader.Get("user", "name", "UNKNOWN") << ", email=" + << reader.Get("user", "email", "UNKNOWN") << ", pi=" + << reader.GetReal("user", "pi", -1) << ", active=" + << reader.GetBoolean("user", "active", true) << "\n"; + return 0; +} diff --git a/ami_test_client/libc-jss/ini/examples/config.def b/ami_test_client/libc-jss/ini/examples/config.def new file mode 100644 index 0000000000..6113252003 --- /dev/null +++ b/ami_test_client/libc-jss/ini/examples/config.def @@ -0,0 +1,8 @@ +// CFG(section, name, default) + +CFG(protocol, version, "0") + +CFG(user, name, "Fatty Lumpkin") +CFG(user, email, "fatty@lumpkin.com") + +#undef CFG diff --git a/ami_test_client/libc-jss/ini/examples/ini_dump.c b/ami_test_client/libc-jss/ini/examples/ini_dump.c new file mode 100644 index 0000000000..87253ee12d --- /dev/null +++ b/ami_test_client/libc-jss/ini/examples/ini_dump.c @@ -0,0 +1,40 @@ +/* ini.h example that simply dumps an INI file without comments */ + +#include +#include +#include "../ini.h" + +static int dumper(void* user, const char* section, const char* name, + const char* value) +{ + static char prev_section[50] = ""; + + if (strcmp(section, prev_section)) { + printf("%s[%s]\n", (prev_section[0] ? "\n" : ""), section); + strncpy(prev_section, section, sizeof(prev_section)); + prev_section[sizeof(prev_section) - 1] = '\0'; + } + printf("%s = %s\n", name, value); + return 1; +} + +int main(int argc, char* argv[]) +{ + int error; + + if (argc <= 1) { + printf("Usage: ini_dump filename.ini\n"); + return 1; + } + + error = ini_parse(argv[1], dumper, NULL); + if (error < 0) { + printf("Can't read '%s'!\n", argv[1]); + return 2; + } + else if (error) { + printf("Bad config file (first error on line %d)!\n", error); + return 3; + } + return 0; +} diff --git a/ami_test_client/libc-jss/ini/examples/ini_example.c b/ami_test_client/libc-jss/ini/examples/ini_example.c new file mode 100644 index 0000000000..09735724d4 --- /dev/null +++ b/ami_test_client/libc-jss/ini/examples/ini_example.c @@ -0,0 +1,44 @@ +/* Example: parse a simple configuration file */ + +#include +#include +#include +#include "../ini.h" + +typedef struct +{ + int version; + const char* name; + const char* email; +} configuration; + +static int handler(void* user, const char* section, const char* name, + const char* value) +{ + configuration* pconfig = (configuration*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("protocol", "version")) { + pconfig->version = atoi(value); + } else if (MATCH("user", "name")) { + pconfig->name = strdup(value); + } else if (MATCH("user", "email")) { + pconfig->email = strdup(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +int main(int argc, char* argv[]) +{ + configuration config; + + if (ini_parse("test.ini", handler, &config) < 0) { + printf("Can't load 'test.ini'\n"); + return 1; + } + printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n", + config.version, config.name, config.email); + return 0; +} diff --git a/ami_test_client/libc-jss/ini/examples/ini_xmacros.c b/ami_test_client/libc-jss/ini/examples/ini_xmacros.c new file mode 100644 index 0000000000..a2cab43b64 --- /dev/null +++ b/ami_test_client/libc-jss/ini/examples/ini_xmacros.c @@ -0,0 +1,46 @@ +/* Parse a configuration file into a struct using X-Macros */ + +#include +#include +#include "../ini.h" + +/* define the config struct type */ +typedef struct { + #define CFG(s, n, default) char *s##_##n; + #include "config.def" +} config; + +/* create one and fill in its default values */ +config Config = { + #define CFG(s, n, default) default, + #include "config.def" +}; + +/* process a line of the INI file, storing valid values into config struct */ +int handler(void *user, const char *section, const char *name, + const char *value) +{ + config *cfg = (config *)user; + + if (0) ; + #define CFG(s, n, default) else if (strcmp(section, #s)==0 && \ + strcmp(name, #n)==0) cfg->s##_##n = strdup(value); + #include "config.def" + + return 1; +} + +/* print all the variables in the config, one per line */ +void dump_config(config *cfg) +{ + #define CFG(s, n, default) printf("%s_%s = %s\n", #s, #n, cfg->s##_##n); + #include "config.def" +} + +int main(int argc, char* argv[]) +{ + if (ini_parse("test.ini", handler, &Config) < 0) + printf("Can't load 'test.ini', using defaults\n"); + dump_config(&Config); + return 0; +} diff --git a/ami_test_client/libc-jss/ini/examples/test.ini b/ami_test_client/libc-jss/ini/examples/test.ini new file mode 100644 index 0000000000..680c3b9aa5 --- /dev/null +++ b/ami_test_client/libc-jss/ini/examples/test.ini @@ -0,0 +1,10 @@ +; Test config file for ini_example.c and INIReaderTest.cpp + +[protocol] ; Protocol configuration +version=6 ; IPv6 + +[user] +name = Bob Smith ; Spaces around '=' are stripped +email = bob@smith.com ; And comments (like this) ignored +active = true ; Test a boolean +pi = 3.14159 ; Test a floating point number diff --git a/ami_test_client/libc-jss/ini/extra/Makefile.static b/ami_test_client/libc-jss/ini/extra/Makefile.static new file mode 100644 index 0000000000..0d6519e384 --- /dev/null +++ b/ami_test_client/libc-jss/ini/extra/Makefile.static @@ -0,0 +1,19 @@ +# Simple makefile to build inih as a static library using g++ + +SRC = ../ini.c +OBJ = $(SRC:.c=.o) +OUT = libinih.a +INCLUDES = -I.. +CCFLAGS = -g -O2 +CC = g++ + +default: $(OUT) + +.c.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(EXTRACCFLAGS) -c $< -o $@ + +$(OUT): $(OBJ) + ar rcs $(OUT) $(OBJ) $(EXTRAARFLAGS) + +clean: + rm -f $(OBJ) $(OUT) diff --git a/ami_test_client/libc-jss/ini/ini.c b/ami_test_client/libc-jss/ini/ini.c new file mode 100644 index 0000000000..8cfee93a23 --- /dev/null +++ b/ami_test_client/libc-jss/ini/ini.c @@ -0,0 +1,176 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, + int (*handler)(void*, const char*, const char*, + const char*), + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through file line by line */ + while (fgets(line, INI_MAX_LINE, file) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, + int (*handler)(void*, const char*, const char*, const char*), + void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} diff --git a/ami_test_client/libc-jss/ini/ini.h b/ami_test_client/libc-jss/ini/ini.h new file mode 100644 index 0000000000..4ad8c649ed --- /dev/null +++ b/ami_test_client/libc-jss/ini/ini.h @@ -0,0 +1,72 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +http://code.google.com/p/inih/ + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + ConfigParser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/ami_test_client/libc-jss/ini/test.ini b/ami_test_client/libc-jss/ini/test.ini new file mode 100644 index 0000000000..4069546e6b --- /dev/null +++ b/ami_test_client/libc-jss/ini/test.ini @@ -0,0 +1,6 @@ +[protocol] +version = 5 ; próba komment + +[user] +name = Gipsz Jakab ; comment +email = gip@szja.kab ; comment diff --git a/ami_test_client/libc-jss/ini/test_ini.c b/ami_test_client/libc-jss/ini/test_ini.c new file mode 100644 index 0000000000..9c0f46284a --- /dev/null +++ b/ami_test_client/libc-jss/ini/test_ini.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +#include "ini.h" + +typedef struct { + int version; + const char* name; + const char* email; +} configuration; + +static int handler(void* user, const char* section, const char* name, + const char* value) +{ + configuration *pconfig = (configuration*)user; + + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + if (MATCH("protocol", "version")) { + pconfig->version = atoi(value); + } else if (MATCH("user", "name")) { + pconfig->name = strdup(value); + } else if (MATCH("user", "email")) { + pconfig->email = strdup(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} + +int main (int argc, char* argv[]) { + configuration config; + + if (ini_parse("test.ini", handler, &config) < 0) { + printf("Can't load 'test.ini'\n"); + return 1; + } + printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n", + config.version, config.name, config.email); + return 0; +} + + diff --git a/ami_test_client/libc-jss/ini/tests/bad_comment.ini b/ami_test_client/libc-jss/ini/tests/bad_comment.ini new file mode 100644 index 0000000000..d4bab4ae8b --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/bad_comment.ini @@ -0,0 +1 @@ +This is an error diff --git a/ami_test_client/libc-jss/ini/tests/bad_multi.ini b/ami_test_client/libc-jss/ini/tests/bad_multi.ini new file mode 100644 index 0000000000..3ec342f21e --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/bad_multi.ini @@ -0,0 +1 @@ + indented diff --git a/ami_test_client/libc-jss/ini/tests/bad_section.ini b/ami_test_client/libc-jss/ini/tests/bad_section.ini new file mode 100644 index 0000000000..689a4e5c77 --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/bad_section.ini @@ -0,0 +1,5 @@ +[section1] +name1=value1 +[section2 +[section3 ; comment ] +name2=value2 diff --git a/ami_test_client/libc-jss/ini/tests/baseline_multi.txt b/ami_test_client/libc-jss/ini/tests/baseline_multi.txt new file mode 100644 index 0000000000..eb872973fa --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/baseline_multi.txt @@ -0,0 +1,47 @@ +no_file.ini: e=-1 user=0 +... [section1] +... one=This is a test; +... two=1234; +... [ section 2 ] +... happy=4; +... sad=; +... [comment_test] +... test1=1;2;3; +... test2=2;3;4;this won't be a comment, needs whitespace before ';'; +... test;3=345; +... test4=4#5#6; +... [colon_tests] +... Content-Type=text/html; +... foo=bar; +... adams=42; +normal.ini: e=0 user=101 +... [section1] +... name1=value1; +... name2=value2; +bad_section.ini: e=3 user=102 +bad_comment.ini: e=1 user=102 +... [section] +... a=b; +... user=parse_error; +... c=d; +user_error.ini: e=3 user=104 +... [section1] +... single1=abc; +... multi=this is a; +... multi=multi-line value; +... single2=xyz; +... [section2] +... multi=a; +... multi=b; +... multi=c; +... [section3] +... single=ghi; +... multi=the quick; +... multi=brown fox; +... name=bob smith; +multi_line.ini: e=0 user=105 +bad_multi.ini: e=1 user=105 +... [bom_section] +... bom_name=bom_value; +... key“=value“; +bom.ini: e=0 user=107 diff --git a/ami_test_client/libc-jss/ini/tests/baseline_single.txt b/ami_test_client/libc-jss/ini/tests/baseline_single.txt new file mode 100644 index 0000000000..e6380381fe --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/baseline_single.txt @@ -0,0 +1,43 @@ +no_file.ini: e=-1 user=0 +... [section1] +... one=This is a test; +... two=1234; +... [ section 2 ] +... happy=4; +... sad=; +... [comment_test] +... test1=1;2;3; +... test2=2;3;4;this won't be a comment, needs whitespace before ';'; +... test;3=345; +... test4=4#5#6; +... [colon_tests] +... Content-Type=text/html; +... foo=bar; +... adams=42; +normal.ini: e=0 user=101 +... [section1] +... name1=value1; +... name2=value2; +bad_section.ini: e=3 user=102 +bad_comment.ini: e=1 user=102 +... [section] +... a=b; +... user=parse_error; +... c=d; +user_error.ini: e=3 user=104 +... [section1] +... single1=abc; +... multi=this is a; +... single2=xyz; +... [section2] +... multi=a; +... [section3] +... single=ghi; +... multi=the quick; +... name=bob smith; +multi_line.ini: e=4 user=105 +bad_multi.ini: e=1 user=105 +... [bom_section] +... bom_name=bom_value; +... key“=value“; +bom.ini: e=0 user=107 diff --git a/ami_test_client/libc-jss/ini/tests/bom.ini b/ami_test_client/libc-jss/ini/tests/bom.ini new file mode 100644 index 0000000000..44c519f475 --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/bom.ini @@ -0,0 +1,3 @@ +[bom_section] +bom_name=bom_value +key“ = value“ diff --git a/ami_test_client/libc-jss/ini/tests/multi_line.ini b/ami_test_client/libc-jss/ini/tests/multi_line.ini new file mode 100644 index 0000000000..b00f086be7 --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/multi_line.ini @@ -0,0 +1,15 @@ +[section1] +single1 = abc +multi = this is a + multi-line value +single2 = xyz +[section2] +multi = a + b + c +[section3] +single: ghi +multi: the quick + brown fox +name = bob smith ; comment line 1 + ; comment line 2 diff --git a/ami_test_client/libc-jss/ini/tests/normal.ini b/ami_test_client/libc-jss/ini/tests/normal.ini new file mode 100644 index 0000000000..bd5bcd7534 --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/normal.ini @@ -0,0 +1,25 @@ +; This is an INI file +[section1] ; section comment +one=This is a test ; name=value comment +two = 1234 +; x=y + +[ section 2 ] +happy = 4 +sad = + +[empty] +; do nothing + +[comment_test] +test1 = 1;2;3 ; only this will be a comment +test2 = 2;3;4;this won't be a comment, needs whitespace before ';' +test;3 = 345 ; key should be "test;3" +test4 = 4#5#6 ; '#' only starts a comment at start of line +#test5 = 567 ; entire line commented + # test6 = 678 ; entire line commented, except in MULTILINE mode + +[colon_tests] +Content-Type: text/html +foo:bar +adams : 42 diff --git a/ami_test_client/libc-jss/ini/tests/unittest.bat b/ami_test_client/libc-jss/ini/tests/unittest.bat new file mode 100644 index 0000000000..d0e84cfcc0 --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/unittest.bat @@ -0,0 +1,2 @@ +@call tcc ..\ini.c -I..\ -run unittest.c > baseline_multi.txt +@call tcc ..\ini.c -I..\ -DINI_ALLOW_MULTILINE=0 -run unittest.c > baseline_single.txt diff --git a/ami_test_client/libc-jss/ini/tests/unittest.c b/ami_test_client/libc-jss/ini/tests/unittest.c new file mode 100644 index 0000000000..97643cafbe --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/unittest.c @@ -0,0 +1,58 @@ +/* inih -- unit tests + +This works simply by dumping a bunch of info to standard output, which is +redirected to an output file (baseline_*.txt) and checked into the Subversion +repository. This baseline file is the test output, so the idea is to check it +once, and if it changes -- look at the diff and see which tests failed. + +Here's how I produced the two baseline files (with Tiny C Compiler): + +tcc -DINI_ALLOW_MULTILINE=1 ../ini.c -run unittest.c > baseline_multi.txt +tcc -DINI_ALLOW_MULTILINE=0 ../ini.c -run unittest.c > baseline_single.txt + +*/ + +#include +#include +#include +#include "../ini.h" + +int User; +char Prev_section[50]; + +int dumper(void* user, const char* section, const char* name, + const char* value) +{ + User = (int)user; + if (strcmp(section, Prev_section)) { + printf("... [%s]\n", section); + strncpy(Prev_section, section, sizeof(Prev_section)); + Prev_section[sizeof(Prev_section) - 1] = '\0'; + } + printf("... %s=%s;\n", name, value); + + return strcmp(name, "user")==0 && strcmp(value, "parse_error")==0 ? 0 : 1; +} + +void parse(const char* fname) { + static int u = 100; + int e; + + *Prev_section = '\0'; + e = ini_parse(fname, dumper, (void*)u); + printf("%s: e=%d user=%d\n", fname, e, User); + u++; +} + +int main(void) +{ + parse("no_file.ini"); + parse("normal.ini"); + parse("bad_section.ini"); + parse("bad_comment.ini"); + parse("user_error.ini"); + parse("multi_line.ini"); + parse("bad_multi.ini"); + parse("bom.ini"); + return 0; +} diff --git a/ami_test_client/libc-jss/ini/tests/user_error.ini b/ami_test_client/libc-jss/ini/tests/user_error.ini new file mode 100644 index 0000000000..659638785c --- /dev/null +++ b/ami_test_client/libc-jss/ini/tests/user_error.ini @@ -0,0 +1,4 @@ +[section] +a = b +user = parse_error +c = d diff --git a/ami_test_client/libc-jss/logger.c b/ami_test_client/libc-jss/logger.c new file mode 100644 index 0000000000..8320635634 --- /dev/null +++ b/ami_test_client/libc-jss/logger.c @@ -0,0 +1,165 @@ +/* logger.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "debug.h" +#include "misc.h" + +#include "utlist.h" +#include "misc.h" + +//~ #define concat(dst, src) strncat((dst), (src), ((sizeof(dst)) - strlen(dst) - 1)) +//~ #define concatf(dst, ...) snprintf((dst) + strlen((dst)), sizeof((dst)) - strlen((dst)), __VA_ARGS__) + +static char *logfile = NULL; +static char logfile_buf[128]; + +/* TODO: rendbe tenni ezt a marhaságot valami kultúrált megoldással úgy, +hogy a hívó függvény által átadott stringet lemásoljuk és helyben a +másolatot kezeljük. Azért, hogy a hívó utána a saját mutatójával azt +csinálhasson, amit csak akar. Kezelni a NULL-t, a default értékeket és +valami kultúrált köntöst adni neki, ne ezt a három változós izét. */ +static char *timestamp_format_default = "%a %H:%M:%S"; +static char *timestamp_format = "%a %H:%M:%S"; +static char timestamp_format_buf[128]; + +static int initialised = 0; +struct timeval first_time = {0, 0}; +char ela[16]; + +typedef struct el { + struct timeval tm; + char *line; + struct el *prev; /* needed for a doubly-linked list only */ + struct el *next; /* needed for singly- or doubly-linked lists */ +} el; + +/* Ha a tv (első paraméter) meg van adva, akkor nincs gettimeofday hívás, hanem + * a tv-ben beállított idő lesz a mérvadó. Ha a tv értéke NULL, akkor a + * jelenlegi idővel (gettimeofday) számolunk. Ha meg van adva a ptm, akkor az is + * be lesz állítva. Ha az elapsed nem NULL, akkor kiszámolja az indítástól + * eltelt időt, és visszaad egy mutatót, ami a kiszámolt érték ssss.mmmu + * formátumú stringre mutat. + */ +static struct timeval get_time (struct timeval *tv, struct tm **ptm, char *elapsed) { + struct timeval mytv; + + if (tv == NULL) { + gettimeofday(&mytv, NULL); + } else { + mytv.tv_sec = tv->tv_sec; + mytv.tv_usec = tv->tv_usec; + } + + if (tv) + *tv = mytv; + + if (ptm) + *ptm = localtime(&mytv.tv_sec); // thread safe? + + if (elapsed) { + long int delta = ((mytv.tv_sec * 1000000) + mytv.tv_usec) - ((first_time.tv_sec * 1000000) + first_time.tv_usec); + snprintf(elapsed, 8, "%3.3f", (float)delta / 1000000); + } + + return mytv; +} + +void con_init () { + if (initialised) + return; + first_time = get_time(NULL, NULL, NULL); + initialised = 1; + return; +} + +void con_logfile (const char *file) { + if (file == NULL) { + logfile = NULL; + return; + } + strncpy(logfile_buf, file, sizeof(logfile_buf)); + logfile = logfile_buf; +} + +void con_timestamp_format (const char *format) { + if (format == NULL) { + timestamp_format = timestamp_format_default; + return; + } + strncpy(timestamp_format_buf, format, sizeof(timestamp_format_buf)); + timestamp_format = timestamp_format_buf; +} + +// gány, memóriazabáló függvény :) +void _con_writef (enum con_callmode cm, char *file, int line, const char *function, const char *fmt, ...) { + if (!initialised) + con_init(); + + // botrányos deklarációk + char tmp[1024] = {0}; // ide kerül a végeredmény + char tmp2[1024]; // va_arg szöveges kimenete + char timestamp[64]; // timestamp + char elapsed[64]; // elapsed + + va_list ap; + va_start(ap, fmt); + vsnprintf(tmp2, sizeof(tmp2), fmt, ap); + va_end(ap); + + // TODO: ezt az idő lekérdezős részt külön függvénybe kell rakni! + struct timeval tv; + struct tm *ptm; + tv = get_time(NULL, &ptm, NULL); // idő, ami alapján a továbbiakban számolunk + strftime(timestamp, sizeof(timestamp), timestamp_format, ptm); // szöveges timestamp + get_time(&tv, &ptm, elapsed); // elapsed lekérdezése TODO: ez borzalmas, átírni!!! + + switch (cm) { + case CON_CALLMODE_CONFT: + case CON_CALLMODE_CONFTN: + concatf(tmp, "%s %s", timestamp, tmp2); + break; + + case CON_CALLMODE_CONF: + case CON_CALLMODE_CONFN: + strncpy(tmp, tmp2, sizeof(tmp) - 1); + break; + + case CON_CALLMODE_DEBUG: + concatf(tmp, "%s /%s/ [%s:%d %s]: %s", timestamp, elapsed, file, line, function, tmp2); + break; + } + + chomp(tmp); + + // Kiiratás + printf("%s", tmp); + if (cm != CON_CALLMODE_CONFTN && cm != CON_CALLMODE_CONFN) + printf("\n"); + fflush(stdout); + + if (logfile) { + FILE *f = fopen(logfile, "a"); + if (f != NULL) { + fprintf(f, "%s", tmp); + if (cm != CON_CALLMODE_CONFTN && cm != CON_CALLMODE_CONFN) + fprintf(f, "\n"); + fclose(f); + } + } + +} + diff --git a/ami_test_client/libc-jss/logger.h b/ami_test_client/libc-jss/logger.h new file mode 100644 index 0000000000..19d0ead585 --- /dev/null +++ b/ami_test_client/libc-jss/logger.h @@ -0,0 +1,45 @@ +/* logger.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + conf(...) kiírás időbélyeg nélkül, újsorral + confn(...) kiírás időbélyeg nélkül, újsor nélkül + conft(...) kiírás időbélyeggel, újsorral + conftn(...) kiírás időbélyeggel, újsor nélkül +*/ + +enum con_callmode { + CON_CALLMODE_CONF, + CON_CALLMODE_CONFN, + CON_CALLMODE_CONFT, + CON_CALLMODE_CONFTN, + CON_CALLMODE_DEBUG, +}; + +#ifndef LOGGER_H_LOADED +#define LOGGER_H_LOADED +void con_init (); +//~ void conft (const char *fmt, ...); +void con_logfile (const char *file); +void con_timestamp_format (const char *format); +void _con_writef (enum con_callmode cm, char *file, int line, const char *function, const char *fmt, ...); +#endif + + +// ha a DEBUG makró 1, akkor a debug() makrók életbe lépnek, +// ellenkező esetben a kódba sem kerül bele :) +#ifdef CON_DEBUG +#define con_debug(...) _con_writef(CON_CALLMODE_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +#else +#define con_debug(...) +#endif + +#define conf(...) _con_writef(CON_CALLMODE_CONF, NULL, 0, NULL, __VA_ARGS__) +#define confn(...) _con_writef(CON_CALLMODE_CONFN, NULL, 0, NULL, __VA_ARGS__) +#define conft(...) _con_writef(CON_CALLMODE_CONFT, NULL, 0, NULL, __VA_ARGS__) +#define conftn(...) _con_writef(CON_CALLMODE_CONFTN, NULL, 0, NULL, __VA_ARGS__) diff --git a/ami_test_client/libc-jss/logger_test.c b/ami_test_client/libc-jss/logger_test.c new file mode 100644 index 0000000000..99991bbd0f --- /dev/null +++ b/ami_test_client/libc-jss/logger_test.c @@ -0,0 +1,43 @@ +/* logger_test.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#define CON_DEBUG + +#include "logger.h" +#include +#include + +int main () { + + con_logfile("logger_test.log"); + con_timestamp_format("%a %H:%M:%S"); + + conft("zsiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiir"); + conft("yeahhhh"); + conft("ok"); + + FILE *f; + if (!(f = fopen("/proc/ioports", "r"))) { + perror("fopen"); + return 1; + } + + char tmp[128]; + int i = 0; + while (fgets(tmp, sizeof(tmp), f)) { + con_debug("i=%d %s", i, tmp); + i++; + } + + + + fclose(f); + + return 0; +} + diff --git a/ami_test_client/libc-jss/misc.c b/ami_test_client/libc-jss/misc.c new file mode 100644 index 0000000000..fc13db2bb8 --- /dev/null +++ b/ami_test_client/libc-jss/misc.c @@ -0,0 +1,285 @@ +/* misc.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* altalanos fuggvenyek */ + +#include +#include +#include +#include // inet_pton() függvény +#include + +#include "debug.h" + +char *chomp (char *s) { + if (s == NULL) + return NULL; + size_t len = strlen(s); + + int n; + for (n = len - 1; n >= 0; n--) { + if (s[n] == '\n' || s[n] == '\r') + s[n] = '\0'; + else + return s; + } + return s; +} + +/* +fmtsub - mintából, változó-érték párokból és készít stringet behelyettesítéssel + +paraméterek: + dest ebbe a stringbe menti el az eredményt + size dest mérete + pattern minta + args változó-érték táblázat, nullával lezárva + +visszatérési érték: + 0 sikerült a behelyettesítés + -1 valamelyik mutató == NULL vagy a size == 0 + +A táblázat megadása: + +const char *args[][2] = { + {"%n", "2463434"}, + {"%v", "0.9.6-rc2"}, + {"version", "Linux 2.6.32"}, + {"boo", "baa"}, + {0}}; */ +int fmtsub (char *dest, size_t size, const char *pattern, const char *args[][2]) { + char buf[4096]; +//~ debs(dest); debi(size); debs(pattern); debi(args); + if (dest == NULL || pattern == NULL || args == NULL || size == 0) + return -1; + + // ha a pattern üres string, akkor üres stringet adunk vissza + if (strlen(pattern) == 0) { + strcpy(dest, ""); + return 0; + } + + int pospat, posbuf, a; + int lenpat = strlen(pattern); + buf[0] = '\0'; + // byteonként végigmegyünk a pattern stringen + for (pospat = 0, posbuf = 0; pospat < lenpat && posbuf < sizeof(buf) - 1;) { + const char *subpat = &pattern[pospat]; + int match = 0; + + // végigmegyünk az args táblázaton + for (a = 0; args[a][0] != 0; a++) { + const char *var = args[a][0]; // "%n" + const char *exp = args[a][1]; // "2463434" + + // ha a változónév megtalálható a pattern jelenlegi pozíciójánál + if (strlen(var) != 0 && !strncmp(subpat, var, strlen(var))) { + if (exp != NULL) { + // bemásoljuk a változó értékét a bufferbe + strncat(buf, exp, sizeof(buf) - posbuf - 1); + posbuf += strlen(exp); + } + + /* ezen a ponton a posbuf nagyobb lehet, mint a buf, ami + igen kellemetlenül érintené a lenti buf[posbuf] = '\0' + értékadást, ezért a posbuf pozícionálót beállítjuk a buf + utolsó byte-jára. ide fog kerülni a NULL. */ + if (posbuf >= sizeof(buf) - 1) + posbuf = sizeof(buf) - 1; + + // pattern pozícióját a változónév utáni karakterre állítjuk + pospat += strlen(var); + + // kihagyjuk a lenti if-et + match = 1; + break; + } + } + + // 1 bájt másolása pattern-ből buf-ba + if (!match) { + buf[posbuf] = pattern[pospat]; + buf[posbuf+1] = '\0'; + pospat++; + posbuf++; + } + } + + buf[posbuf] = '\0'; // lezáró NULL + strncpy(dest, buf, size - 1); + return 0; +} + +// http://en.wikipedia.org/wiki/ROT13 +// paraméter az a string, amit módosítani kell +// visszatérés ugyan az, mint a paraméter kompatibilitási izé miatt +char *encode_rot13 (char *s) { + if (s == NULL) + return NULL; + + int i; + for (i = 0; s[i]; i++) { + if (s[i] >= 'a' && s[i] <= 'm') { s[i] += 13; continue; } + if (s[i] >= 'A' && s[i] <= 'M') { s[i] += 13; continue; } + if (s[i] >= 'n' && s[i] <= 'z') { s[i] -= 13; continue; } + if (s[i] >= 'N' && s[i] <= 'Z') { s[i] -= 13; continue; } + } + return s; +} + +// szóközöket és tabokat vág le a megadott string bal és/vagy jobb oldaláról +// trim - mindkét oldalról vág +// ltrim - bal oldalról vág +// rtrim - jobb oldalról vág +char *_trim (char *s, int trim_from_left, int trim_from_right) { + if (s == NULL) + return NULL; + + int n, i; + size_t len = strlen(s); + + if (trim_from_right) { + for (n = len - 1; n >= 0; n--) { + if (s[n] == 32 || s[n] == '\t') { + s[n] = '\0'; + len--; + } else { + break; + } + } + } + + if (trim_from_left) { + for (n = 0; n < len; n++) + if (s[n] != 32 && s[n] != '\t') + break; + + if (n > 0) { + for (i = n; i < len; i++) + s[i-n] = s[i]; + + for (i = len - n; i < len; i++) + s[i] = '\0'; + } + } + return s; +} + +/* megszámlálja a elemeket */ +static int split_get_size (char *buffer, int delimeter) { + int c = 0, i = 0; + if (buffer[ 0 ] == 0) + return 0; + while (buffer[i] != 0) { + if (buffer[i] == delimeter) + c++; + i++; + } + /* egyet hozzáadok, mert a "hello" is értéknek számít, hiába nincs benne ';' */ + return c + 1; +} + +/* visszatér egy pointer tömbbel, ami az elemekre mutat, a tömböt 0 pointerrel zárja */ +char **split (char *buffer, int delimeter) { + int size = split_get_size(buffer, delimeter); + /* helyfoglalás a pointer tömbnek */ + char **res = (char **)malloc(sizeof(char *) * (size + 1)); + int i=0; + int p=0; + while (buffer[i] != 0) { + /* a sor elejét eltárolom */ + res[p++] = buffer + i; + /* a sor végét megkeresem */ + do { + i++; + } while (buffer[i] != 0 && buffer[i] != delimeter); + /* felülírjuk a ';'-t 0-val */ + if (buffer[i] != 0) { + buffer[i] = 0; + i++; + } + } + + /* null pointerrel zárjuk */ + res[p] = 0; + return res; +} + +// 1-et ad vissza, ha érvényes a kapott IP cím +// 0-át ha nem +int is_valid_ip (const char *ip) { + struct sockaddr_in sa; + int result = inet_pton(AF_INET, ip, &(sa.sin_addr)); + return result != 0; +} + +void die (const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(-1); +} + +void read_lines_from_stdin (char *dst, int size) { + char *line = NULL; // ideiglenes buffer a getline-nak + size_t len = 0; + ssize_t read; + int remaining_size; // dst stringbe még ennyi byte fér el + + remaining_size = size - 1; + + while ((read = getline(&line, &len, stdin)) != -1) { + strncat(dst, line, remaining_size); + remaining_size -= read; + if (remaining_size <= 0) + break; + } + chomp(dst); // utolsó \n karakter chompolása + free(line); +} + +char *strcutpbrk (char *str, const char *accept) { + if (str == NULL) + return NULL; + + if (accept == NULL) + return str; + + char *at = strpbrk(str, accept); + if (at != NULL) + *at = '\0'; + + return str; +} + +char *strdelchars (char *str, const char *dels) { + if (!str) + return NULL; + + if (!dels) + return str; + + int len_str = strlen(str); + int len_dels = strlen(dels); + + if (!len_str || !len_dels) + return str; + + int i, j, k = 0; + for (i = 0; i < len_str; i++) { + for (j = 0; j < len_dels; j++) + if (str[i] == dels[j]) + break; + if (j == len_dels) // ha nem volt break + str[k++] = str[i]; + } + str[k] = '\0'; + return str; +} + diff --git a/ami_test_client/libc-jss/misc.h b/ami_test_client/libc-jss/misc.h new file mode 100644 index 0000000000..75ef546e54 --- /dev/null +++ b/ami_test_client/libc-jss/misc.h @@ -0,0 +1,58 @@ +/* misc.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +char *chomp (char *str); + +#define scpy(dst, src) strncpy((dst), (src), (sizeof(dst)) - 1) + +#define concat(dst, src) strncat((dst), (src), ((sizeof(dst)) - strlen(dst) - 1)) +#define concatf(dst, ...) snprintf((dst) + strlen((dst)), sizeof((dst)) - strlen((dst)), __VA_ARGS__) +int fmtsub (char *dest, size_t size, const char *pattern, const char *args[][2]); +char *encode_rot13 (char *s); + +char *_trim (char *s, int trim_from_left, int trim_from_right); +#define trim(s) _trim(s, 1, 1) +#define ltrim(s) _trim(s, 1, 0) +#define rtrim(s) _trim(s, 0, 1) + +// Az str stringet levágja ott, ahol az accept stringben felsorolt első +// karakterrel találkozik. A levágást úgy oldja meg, hogy az str string +// megfelelő byte-ját átírja 0-ra. A visszatérési érték az str string. Ha az +// accept == NULL, akkor nem piszkálja az str stringet. Ha az str == NULL, +// akkor a visszatérési érték NULL. Például ha az str "Bandi" és az accept +// "cde", akkor az eredmény "Ban" lesz. +char *strcutpbrk (char *str, const char *accept); + +// Nincs még doksi, ezért ide írom... +// A chomp, trim, encode_rot13, strcutpbrk függvények használhatok beágyazva is, tehát: +// char buf[64]; +// encode_rot13(trim(chomp(buf))); + +// visszatér egy pointer tömbbel, ami az elemekre mutat, a tömböt 0 pointerrel zárja. A függvény felülírja a buffer stringben lévő delimetereket. Magyarul módosítja a kapott stringet, ami nem biztos, hogy jó dolog. Pl. char *x = "hello" változóknál segfault. +char **split (char *buffer, int delimeter); + +// 1-et ad vissza, ha érvényes a kapott IP cím +// 0-át ha nem +int is_valid_ip (const char *ip); + +// mint Perl-ben:) +void die (const char *fmt, ...); + +// STDIN-ről beolvasás EOF-ig. A sorokat összefűzi és a dst stringbe menti, +// vigyázva a lezáró NULL karakterre és a dst string hosszára, amit a size-ben +// kell átadni. Az utolsó \n karakterek le lesznek csípve. +void read_lines_from_stdin (char *dst, int size); + +// Az str stringből eltávolítja a dels stringben megtalálható karaktereket. Az +// eltávolítás folyamán az str stringben a byteokat fokozatosan balra rendezi. +// A művelet végén elhelyezi a stringet lezáró NULL karaktert. Az eredmény +// ugyan akkora vagy kisebb méretű string. A visszatérési érték az str string. +// Ha az str NULL, akkor a visszatérési érték NULL. Ha a dels NULL, akkor a +// visszatérési érték az str string. +char *strdelchars (char *str, const char *dels); + diff --git a/ami_test_client/libc-jss/netsocket.c b/ami_test_client/libc-jss/netsocket.c new file mode 100644 index 0000000000..dbf73492bc --- /dev/null +++ b/ami_test_client/libc-jss/netsocket.c @@ -0,0 +1,524 @@ +/* netsocket.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +// TODO: datapipe-nál jól látható, hogy a connect előtt bejött adatok +// nem kerülnek kiküldésre. Valami buffer megoldást kéne alkalmazni. vagy mégsem? +// ... végülis a Centauri nézőpontja szerint connect előtt miért küldenénk adatokat? + +// TODO: gethostbyaddr és gethostbyname függvényeket lecserélni getaddrinfo és +// getnameinfo függvényekre +// +// Figyelem! Amelyik függvényben invoke_callback van, annak a legaljára kell +// destroy_netsocket. Na meg ezt a kommentet átfogalmazni, mert valószínűleg +// baromira nem fogom érteni két hét múlva, hogy mi a szar is akar ez lenni + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netsocket.h" +#include "debug.h" + +static void dummy_callback (netsocket_t *obj, int event) { + printf("dummy_callback called at %s:%d\n", __FILE__, __LINE__); + printf(" event = %d\n mode = %s\n host = %s\n port = %d\n lhost = %s\n lport = %d\n", + event, + (obj->mode == NETSOCKET_SERVER) ? "server" : "client", + obj->host, + obj->port, + obj->lhost, + obj->lport + ); +} + +static void sock_close (netsocket_t *obj) { + if (!obj->sock) + return; + ev_io_stop(obj->loop, &obj->w_in); + ev_io_stop(obj->loop, &obj->w_out); + ev_timer_stop(obj->loop, &obj->w_connect_timeout); + close(obj->sock); + obj->sock = 0; +} + +static void invoke_callback (netsocket_t *obj, int event) { + obj->in_callback++; + obj->callback(obj, event); + obj->in_callback--; + + // késleltetett destroy + if (obj->destroy_request) { + if (!obj->in_callback) { + netsocket_destroy(obj); + } + } +} + + +static void disconnect (netsocket_t *obj, char *reason, int ignore_callback) { + if (obj == NULL) + return; + + sock_close(obj); + if (reason != NULL) + strncpy(obj->disconnect_reason, reason, sizeof(obj->disconnect_reason) - 1); + + if (!ignore_callback) + invoke_callback(obj, NETSOCKET_EVENT_DISCONNECT); + + //~ if (!obj->connected) + //~ return; + obj->connected = 0; + + // automatikusan felszabaditjuk a gyermek objektumot abban az esetben, ha + // az obj kliens modban van es bejovo kapcsolatrol van szo, maskeppen mondva + // akkor, ha egy szerver objektum gyermek objektuma szakad meg + // ilyen objektumot a sock_accept allokal malloccal + + //~ if (obj->mode == NETSOCKET_CLIENT && obj->direction == NETSOCKET_IN) + //~ netsocket_destroy(obj); +} + +/** + * @brief Disconnect from peer, close socket + * @param obj the netsocket object + * @param reason description of disconnect reason + * + * If connection in progress, abort the connection and close the socket. + * If connection was established, disconnect from peer, close socket and + * detach from event loop. Call the callback with NETSOCKET_EVENT_DISCONNECT + * event. Save the "reason" to obj->reason. + */ +void netsocket_disconnect (netsocket_t *obj, char *reason) { + disconnect(obj, reason, 1); // 1 jelzi, hogy nem kérünk callback hívást +} + +void netsocket_disconnect_withevent (netsocket_t *obj, char *reason) { + disconnect(obj, reason, 0); // 0 jelzi, hogy kérünk callback hívást +} + +// ha ezt meghívjuk, akkor a bejövő kapcsolatoknál nem kérdezzük meg +// a kliens IP címének nevét a DNS szervertől +void netsocket_disable_lookup_on_accept (netsocket_t *obj) { + obj->disable_lookup_on_accept = 1; +} + +static int sock_accept (netsocket_t *parent) { + netsocket_t *obj = netsocket_new(dummy_callback, NULL, parent->loop); + + obj->mode = NETSOCKET_CLIENT; + obj->direction = NETSOCKET_IN; + + socklen_t addrlen = sizeof(obj->addr); + obj->sock = accept(parent->sock, (struct sockaddr *) &obj->addr, &addrlen); + if (obj->sock < 0) { // TODO rendes hibakezelés, memória felszabadítás + perror("accept"); // például: Too many open files + netsocket_destroy(obj); + return -1; + } + + // kliens IP cím lekérdezése, ha nincs beállítva a "disable_lookup_on_accept" + if (!parent->disable_lookup_on_accept) + obj->hostent = gethostbyaddr(&obj->addr.sin_addr, sizeof(&obj->addr.sin_addr), AF_INET); + + // ha nincs hostja az IP-nek, akkor a hostent NULL lesz + if (obj->hostent != NULL) + strncpy(obj->host, obj->hostent->h_name, sizeof(obj->host) - 1); + + // IP cím tárolása szöveges formátumban + strncpy(obj->ip, inet_ntoa(obj->addr.sin_addr), sizeof(obj->ip) - 1); + + // ha nincs hostja az IP-nek, akkor az IP lesz a host + if (!strlen(obj->host)) + strncpy(obj->host, obj->ip, sizeof(obj->host) - 1); + + // Kliens portja + obj->port = ntohs(obj->addr.sin_port); + + ev_io_set(&obj->w_in, obj->sock, EV_READ); + ev_io_set(&obj->w_out, obj->sock, EV_WRITE); + + ev_io_start(obj->loop, &obj->w_in); + ev_io_start(obj->loop, &obj->w_out); + + obj->parent = parent; + obj->callback = parent->callback; + obj->userdata = parent->userdata; + strncpy(obj->lhost, parent->lhost, sizeof(obj->lhost)); + obj->lport = parent->lport; + + return 0; +} + +static void w_connect_timeout_cb (EV_P_ ev_io *w, int revents) { + netsocket_t *obj = w->data; + disconnect(obj, "Connection timed out", 0); +} + +static void w_in_cb (EV_P_ ev_io *w, int revents) { + netsocket_t *obj = w->data; + int i; + + // ha a szerver portra csatlakozott új kliens + if (obj->mode == NETSOCKET_SERVER) { + sock_accept(obj); + return; + } + + /* Ide akkor kerülünk, amikor egy kliensről adat érkezik. + * A NETSOCKET_EVENT_CONNECT eseményt és az obj->connected 1-re állítását + * a w_out_cb() függvény okozza, de az valójában a sock_accept() függvényben + * lenne esedékes. Szervernél, amikor várjuk a klienseket, normális esetben + * nincs ezzel semmmi gond, de ha valgrind-el fut, akkor valamiért + * összekeveredik az event loop-ban a sorrend és előbb hívódik meg az + * új kliens objektummal a NETSOCKET_EVENT_READ, mint a NETSOCKET_EVENT_CONNECT. + * Ez a hívó kódjában okozhat kollóziót (és okozott is), ezért ha nincs + * beállítva az obj->connected, de mégis adatot akarnánk beolvasni, akkor + * egyszerűen csak "elnapoljuk" a feladatot. Ekkor az event loop megteszi + * prevenciós körét a w_out_cb() függvényben is, ahol megtörténik a + * NETSOCKET_EVENT_CONNECT és utána ismét visszatér ide. Ezzel kényszerítjük + * ki a helyes sorrendet. + */ + if (!obj->connected) + return; + + // adat beolvasása a kliensről + i = read(obj->sock, obj->inbuf, sizeof(obj->inbuf)); + if (i < 1) { + disconnect(obj, (i == 0) ? "Connection reset by peer" : strerror(errno), 0); + } else { + obj->inbuf_len = i; + invoke_callback(obj, NETSOCKET_EVENT_READ); + } +} + +static int sockerr (int sock) { + int optval = 0; + int err; + socklen_t optlen = sizeof(optval); + err = getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen); + if (err) return err; + return optval; +} + +static void w_out_cb (EV_P_ ev_io *w, int revents) { + netsocket_t *obj = w->data; +//~ printf("Can't connect to %s: %s\n", obj->host, strerror(sockerr(obj->sock))); + obj->err = sockerr(obj->sock); + ev_io_stop(EV_A_ w); + if (obj->err) { + disconnect(obj, strerror(obj->err), 0); + } else { + obj->connected = 1; + ev_timer_stop(obj->loop, &obj->w_connect_timeout); + invoke_callback(obj, NETSOCKET_EVENT_CONNECT); + } +} + +/** + * @brief Make outgoing connection to remote host + * @param obj the netsocket object + * @returns 0 if everything ok, otherwise -1 + * + * Use netsocket as client. The function check the syntax of the "host" and "port" variable and + * lookup the host. If the host resolved successfully, then create an + * non-blocking outgoing socket, call connect(3) and add the socket to + * the event loop. In case of problem, close socket and call callback + * with event NETSOCKET_EVENT_DISCONNECT. + */ +int netsocket_connect (netsocket_t *obj) { + obj->mode = NETSOCKET_CLIENT; + obj->direction = NETSOCKET_OUT; + + if (obj->host == NULL) { + disconnect(obj, "Invalid host", 0); + return -1; + } + + if (obj->port < 1 || obj->port > 65535) { + disconnect(obj, "Invalid port", 0); + return -1; + } + + // TODO aszinkronná tenni a host lookup-ot + if ((obj->hostent = gethostbyname(obj->host)) == NULL) { + disconnect(obj, "Host not found", 0); + return -1; + } + + struct in_addr **pptr; + pptr = (struct in_addr **)obj->hostent->h_addr_list; + strncpy(obj->ip, inet_ntoa(**(pptr++)), sizeof(obj->ip)); + obj->sock = socket(PF_INET, SOCK_STREAM, 0); + + ev_io_set(&obj->w_in, obj->sock, EV_READ); + ev_io_set(&obj->w_out, obj->sock, EV_WRITE); + ev_timer_set(&obj->w_connect_timeout, (float)obj->connect_timeout / 1000, 0); + + ev_io_start(obj->loop, &obj->w_in); + ev_io_start(obj->loop, &obj->w_out); + ev_timer_start(obj->loop, &obj->w_connect_timeout); + + // socket non-block + fcntl(obj->sock, F_SETFL, fcntl(obj->sock, F_GETFL, 0) | O_NONBLOCK); + + bzero(&obj->addr, sizeof(obj->addr)); + obj->addr.sin_family = AF_INET; + obj->addr.sin_port = htons(obj->port); + obj->addr.sin_addr.s_addr = *(long*)(obj->hostent->h_addr); + + connect(obj->sock, (struct sockaddr*)&obj->addr, sizeof(obj->addr)); + if (errno != EINPROGRESS) { + netsocket_disconnect(obj, strerror(errno)); + return -1; + } + return 0; +} + +/** + * @brief Listening for incoming connections + * @param obj the netsocket object + * @returns 0 if everything ok, otherwise -1 + * + * Use netsocket as server. The function check the syntax of + * the "lhost" and "lport" variable and + * lookup the lhost. If the host resolved successfully, then create an + * non-blocking incoming socket, bind to lhost, and add the socket to + * the event loop. If "lhost" is NULL, then "0.0.0.0" is assumed. + * + * When a peer connects to the listening port, create a new netsocket object and inherit + * lhost, lport, userdata and callback variables from the server object. + * Accept the incoming connection, + * add them to the event loop and call the callback with NETSOCKET_EVENT_CONNECT event. + * The callback's object parameter is the newly created client object. The server object's + * address is in obj->parent pointer. For each incoming connection make individually a + * new netsocket object. + */ +int netsocket_listen (netsocket_t *obj) { + obj->mode = NETSOCKET_SERVER; + + if (!strlen(obj->lhost)) + strcpy(obj->lhost, "0.0.0.0"); + + if (obj->lport < 1 || obj->lport > 65535) { + disconnect(obj, "Invalid local port", 0); + return -1; + } + + // TODO aszinkronná tenni a host lookup-ot + if ((obj->hostent = gethostbyname(obj->lhost)) == NULL) { + disconnect(obj, "Local host not found", 0); + return -1; + } + + struct in_addr **pptr; + pptr = (struct in_addr **)obj->hostent->h_addr_list; + strncpy(obj->ip, inet_ntoa(**(pptr++)), sizeof(obj->ip)); + + // TODO: socket függvény hibájának csekkolása + obj->sock = socket(AF_INET, SOCK_STREAM, 0); + + fcntl(obj->sock, F_SETFL, fcntl(obj->sock, F_GETFL, 0) | O_NONBLOCK); + + int optval = 1; + if (setsockopt(obj->sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { + perror("setsockopt"); // TODO normális hibakezelés + return -1; + } + + bzero(&obj->addr, sizeof(obj->addr)); + obj->addr.sin_family = AF_INET; + obj->addr.sin_port = htons(obj->lport); + obj->addr.sin_addr.s_addr = *(long*)(obj->hostent->h_addr); + + if (bind(obj->sock, (struct sockaddr *) &obj->addr, sizeof(obj->addr)) < 0) { + disconnect(obj, "Error on bind()", 0); // TODO rendes hibaüzenet + return -1; + } + + if (listen(obj->sock, 5)) { + disconnect(obj, "Error on listen()", 0); + return -1; + } + + ev_io_set(&obj->w_in, obj->sock, EV_READ); + ev_io_set(&obj->w_out, obj->sock, EV_WRITE); + //~ ev_timer_set(&obj->w_connect_timeout, (float)obj->connect_timeout / 1000, 0); + + ev_io_start(obj->loop, &obj->w_in); + ev_io_start(obj->loop, &obj->w_out); + //~ ev_timer_start(EV_DEFAULT, &obj->w_connect_timeout); + + return 0; +} + +/** + * @brief write data to netsocket socket + * @param obj the netsocket object + * @param data data string + * @param length length of data string + * @returns length of written data + * + * Write data to socket. O yeahhh:) + */ +int netsocket_write (netsocket_t *obj, const char *data, int length) { + int i = 0; + if (!obj->sock) return 0; // véletlenül sem írunk az stdout-ra :) + + /* Úgy néz ki, hogy Linux alatt a send() az igazi. Az MSG_NOSIGNAL nélkül a + rendszer PIPE szignállal kinyírja a szervert abban az esetben, ha "Broken + pipe" állapot lép fel, tehát a socket bezárult (kliens megszakadt) de a + szerver még írni akar rá. Az MSG_NOSIGNAL állítólag csak Linux alatt + létezik. BSD alatt pl. az a megoldás, hogy a setsockopt függvénnyel be kell + állítani az adott socketen a SO_NOSIGPIPE flag-et, majd write() vagy más + függvénnyel lehet a socketre irkálni. További probléma a netsocket + szerkezetéből adódik, ahová mindig eljutok, amikor aszinkron rendszerben + meghívódik egy callback és a callback-en belül van törölve az az objektum, + ami a callback-et meghívta. Ez ugye paradoxon, avagy magad alatt vágod a + fát, C-ben pedig csúnya segfault vagy még rosszabb. Jelen esetben itt a + send() függvény visszatérési értékénél kellene ellenőrizni a "Broken pipe" + esetet és ha bekövetkezik, akkor a netsocket_disconnect() függvénnyel + megszakítani a kapcsolatot és hátrahagyni a "Broken pipe" üzenetet. Ebben az + esetben a netsocket_disconnect meghívja a sock_close függvényt, ami törli a + netsocket objektumot, majd visszatér ide. Ezen a ponton már a netsocket + objektum, amivel eddig dolgoztunk, nem létezik és elképzelhető, hogy érkezik + még egy netsocket_write függvényhívás vagy eleve mivel callback-ben vagyunk, + más is történik a netsocket-tel, ami már végzetes egy nem létező objektumon. + Erre kell kitalálni valami megoldást! Két ötlet van: vagy hardcore módon + minden netsocket függvény a meghívásnál leellenőrzi, hogy a paraméterül + átadott netsocket objektum létezik, írható, stb., és ha nem, akkor visszatér + hibával. A másik módszer pedig, hogy aszinkron módon történik a netsocket + objektumok törlése. A netsocket_disconnect megrendeli a törlést, és majd a + callback lefutása után ténylegesen le is lesz törölve. Ezt az aszinkron + megoldást már csináltam Perl-ben és jól működött, de valahogyan most ezt a + kommentet fogalmazva, a hardcore verzió tűnik a legjobbnak, tehát: minden + azonnal történjen meg, semmi ne blokkolódjon, ha már nem létezik az adott + objektum, akkor hibával térjünk vissza és ne folytassuk a műveletet. Ezt + minden olyan helyen ellenőrizni kell, ahol az objektummal munka van. Ez a + koncepció talán a szálaknál is alkalmazható egy-egy mutex lock körzet alatt. + Végiggondolni a szitut úgy, hogy egy callback-ből hívott netsocket_write + hívja meg a netsocket_disconnect-et:) Praktikusan az, ami miatt ezt a + litániát megírtam. */ + + /* Ötlet. Az aszinkron megoldást végiggondolni jobban! A netsocket_destroy() + meghívására ne törlődjön azonnal a netsocket. Ha callback-ben van éppen, + akkor a callback után történjen a felszabadítás. Vagy dupla mutatót használni + és ellenőrizni a NULL értéket? Faszság:) */ + + // i = write(obj->sock, data, length); // nem szignál-biztos + i = send(obj->sock, data, length, MSG_NOSIGNAL); + return i; +} + +/** + * @brief Create a new netsocket object + * @param callback callback function pointer + * @param userdata user defined pointer + * @param loop event loop + * @returns a new netsocket object + * + * Create and return a malloc'ed netsocket object. Save callback and userdata + * in the netsocket structure. Callback will be called when an event occur. Userdata + * is an user defined pointer. If the optional loop parameter is given, then tell + * the netsocket to use this event loop for async operations. This is useful in + * multithreading environment. If the loop is NULL, then the default event loop + * will be used. + */ +netsocket_t *netsocket_new (void *callback, void *userdata, struct ev_loop *loop) { + netsocket_t *obj = malloc(sizeof(*obj)); + if (obj == NULL) + return NULL; + + //~ printf("netsocket object size = %d\n", sizeof(*obj)); + bzero(obj, sizeof(*obj)); // mindent nullázunk + + // ha meg van adva a loop paraméter, akkor azt használjuk eseménykezelőnek + // ellenkező esetben az alapértelmezett eseménykezelőt + obj->loop = (loop != NULL) ? loop : ev_default_loop(0); + + // default értékek + obj->connect_timeout = 5000; // 5000 millisec + + obj->callback = callback; + obj->userdata = userdata; + + obj->w_in.data = obj; + obj->w_out.data = obj; + obj->w_connect_timeout.data = obj; + ev_init(&obj->w_in, (void*)w_in_cb); + ev_init(&obj->w_out, (void*)w_out_cb); + ev_init(&obj->w_connect_timeout, (void*)w_connect_timeout_cb); + + return obj; +} + +/** + * @brief destroy the netsocket object + * @param obj the netsocket object + * + * Disconnect from peer(s) without call the callback, close sockets, + * free all resources and destroy the netsocket object. + */ +// TODO ha a szerver netsocket-et szabadítjuk fel, akkor az összes hozzá tartozó +// kliens is menjen a levesbe +void netsocket_destroy (netsocket_t *obj) { + // ha nincs objektum, akkor lófaszjóska + if (obj == NULL) + return; + + if (obj->in_callback > 0) { + obj->destroy_request = 1; + return; + } + + disconnect(obj, NULL, 1); + free(obj); +} + +// formázott konzol üzenet kiírása időbélyeggel +int netsocket_printf (netsocket_t *obj, const char *fmt, ...) { + char tmp[8192]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(tmp, sizeof(tmp) - 1, fmt, ap); + va_end(ap); + + return netsocket_write(obj, tmp, strlen(tmp)); +} + +void netsocket_host (netsocket_t *obj, const char *host) { + if (host != NULL) + strncpy(obj->host, host, sizeof(obj->host) - 1); +} + +void netsocket_lhost (netsocket_t *obj, const char *lhost) { + if (lhost != NULL) + strncpy(obj->lhost, lhost, sizeof(obj->lhost) - 1); +} + +void netsocket_port (netsocket_t *obj, int port) { + obj->port = port; +} + +void netsocket_lport (netsocket_t *obj, int lport) { + obj->lport = lport; +} + +int netsocket_is_connected (netsocket_t *obj) { + return obj->connected; +} + diff --git a/ami_test_client/libc-jss/netsocket.h b/ami_test_client/libc-jss/netsocket.h new file mode 100644 index 0000000000..3c637f80e6 --- /dev/null +++ b/ami_test_client/libc-jss/netsocket.h @@ -0,0 +1,78 @@ +/* netsocket.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef NETSOCKET_H_INCLUDED +#define NETSOCKET_H_INCLUDED + +#include +#include + +#define NETSOCKET_IN 1 +#define NETSOCKET_OUT 2 + +#define NETSOCKET_CLIENT 1 +#define NETSOCKET_SERVER 2 + +#define NETSOCKET_TCP 1 +#define NETSOCKET_UNIX 2 + +#define NETSOCKET_STATE_RESOLVING 1 +#define NETSOCKET_STATE_CONNECTING 2 +#define NETSOCKET_STATE_CONNECTED 3 + +#define NETSOCKET_EVENT_ERROR 1 +#define NETSOCKET_EVENT_CONNECT 2 +#define NETSOCKET_EVENT_READ 3 +#define NETSOCKET_EVENT_DISCONNECT 4 + +typedef struct netsocket_t { + char host[128]; + int port; + char lhost[128]; + int lport; + int connect_timeout; + char ip[20]; + int sock; + struct hostent *hostent; + struct sockaddr_in addr; + char inbuf[1024]; + int inbuf_len; + ev_io w_out; + ev_io w_in; + ev_timer w_connect_timeout; + void (*callback)(void*, int); + void *userdata; // user data + int err; + int event; + char disconnect_reason[128]; + int connected; + int mode; + struct netsocket_t *parent; + int direction; + int destroy_request; + int in_callback; + int disable_lookup_on_accept; + struct ev_loop *loop; +} netsocket_t; + +netsocket_t *netsocket_new (void *callback, void *userdata, struct ev_loop *loop); +void netsocket_destroy (netsocket_t *obj); +int netsocket_connect (netsocket_t *obj); +int netsocket_listen (netsocket_t *obj); +void netsocket_disconnect (netsocket_t *obj, char *reason); +void netsocket_disconnect_withevent (netsocket_t *obj, char *reason); +int netsocket_write (netsocket_t *obj, const char *data, int length); +int netsocket_printf (netsocket_t *obj, const char *fmt, ...); +void netsocket_disable_lookup_on_accept (netsocket_t *obj); +void netsocket_host (netsocket_t *obj, const char *host); +void netsocket_lhost (netsocket_t *obj, const char *host); +void netsocket_port (netsocket_t *obj, int port); +void netsocket_lport (netsocket_t *obj, int port); +int netsocket_is_connected (netsocket_t *obj); + +#endif // #ifndef NETSOCKET_H_INCLUDED diff --git a/ami_test_client/libc-jss/pipe.c b/ami_test_client/libc-jss/pipe.c new file mode 100644 index 0000000000..02a28ef6db --- /dev/null +++ b/ami_test_client/libc-jss/pipe.c @@ -0,0 +1,200 @@ +/* pipe.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "netsocket.h" + +static char *lhost; // = "0.0.0.0"; +static int lport; // = 1111; + +static char *host; // = "irc.jss.hu"; +static int port; // = 6667; + +//~ static int i = 0; +int n = 0; + +//~ static netsocket_t *cnetsocket; // client +static netsocket_t *snetsocket; // server + +static ev_timer halfsec_timer; +//~ static ev_timer reconnect_timer; + + +static void halfsec_timeout (EV_P_ ev_timer *w, int revents) { + //~ if (i == 2) netsocket_disconnect(netsocket, "ötödik másodperc miatt"); + //~ if (i == 7) netsocket_connect(netsocket, "pingoly", 5555); + //~ printf("timeout %d\n", n++); +} + +//~ static void reconnect_timeout (EV_P_ ev_timer *w, int revents) { + //~ ev_timer_stop(EV_DEFAULT, &reconnect_timer); + //~ printf("Connecting [#%d] to %s:%d...\n", i++, host, port); + //~ netsocket_connect(cnetsocket, host, port); +//~ } + +static void ccallback (netsocket_t *netsocket, int event) { + //~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason); + + switch (event) { + case NETSOCKET_EVENT_CONNECT: + printf("Connected to %s (%s) port %d\n", + netsocket->host, + netsocket->ip, + netsocket->port + ); + //~ char *msg = "Szevasz szerver!\n"; + //~ netsocket_write(netsocket, msg, strlen(msg)); + break; + + case NETSOCKET_EVENT_DISCONNECT: + //~ ev_timer_again(EV_DEFAULT, &reconnect_timer); + //~ ev_timer_start(EV_DEFAULT, &reconnect_timer); + if (netsocket->connected) { + printf("Disconnected from %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + } else { + printf("Can't connect to %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + } + netsocket_destroy(netsocket->userdata); + netsocket_destroy(netsocket); + break; + + case NETSOCKET_EVENT_READ: + printf("Got %d bytes from %s:%d\n", + netsocket->inbuf_len, + netsocket->host, + netsocket->port + ); + //~ printf("inbuf = %s\n", netsocket->inbuf); + netsocket_write(netsocket->userdata, netsocket->inbuf, netsocket->inbuf_len); + break; + + default: + printf("Unhandled event: %d\n", event); + break; + } + + //~ return; + //~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n", + //~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err)); +} + +static void scallback (netsocket_t *netsocket, int event) { + //~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason); + + switch (event) { + case NETSOCKET_EVENT_CONNECT: + // a netsocket az új kliens socket + // a szülőt, tehát azt a netsocket objektumot, ami a szerver + // és figyel a porton, azt a netsocket->parent lehet elérni + // a netsocket->userdata megegyezik a szülő userdata-val + printf("Connect from %s:%d (%s)\n", + netsocket->ip, + netsocket->port, + netsocket->host + ); + //~ char *msg = "Szevasz kliens!\n"; + //~ netsocket_write(netsocket, msg, strlen(msg)); + + netsocket_t *newnetsocket; + newnetsocket = netsocket_new(ccallback, netsocket, NULL); + netsocket_host(newnetsocket, host); + netsocket_port(newnetsocket, port); + netsocket->userdata = newnetsocket; + netsocket_connect(netsocket->userdata); + //~ netsocket_disconnect(netsocket, "megszakadsz"); + //~ netsocket_destroy(netsocket); + break; + + case NETSOCKET_EVENT_DISCONNECT: + //~ ev_timer_again(EV_DEFAULT, &reconnect_timer); + //~ ev_timer_start(EV_DEFAULT, &reconnect_timer); + if (netsocket->connected) { + printf("Disconnected from %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + netsocket_destroy(netsocket->userdata); + netsocket_destroy(netsocket); + } else { + printf("Can't listen on %s:%d: %s\n", + netsocket->host, + netsocket->port, + netsocket->disconnect_reason + ); + exit(255); + } + break; + + case NETSOCKET_EVENT_READ: + printf("Got %d bytes from %s:%d\n", + netsocket->inbuf_len, + netsocket->host, + netsocket->port + ); + //~ printf("inbuf = %s\n", netsocket->inbuf); + netsocket_write(netsocket->userdata, netsocket->inbuf, netsocket->inbuf_len); + break; + + default: + printf("Unhandled event: %d\n", event); + break; + } + + //~ return; + //~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n", + //~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err)); +} + +int main (int argc, char **argv) { + + + if (argc < 5) { + printf("usage: %s \n", argv[0]); + exit(255); + } + + lhost = argv[1]; + lport = atoi(argv[2]); + host = argv[3]; + port = atoi(argv[4]); + + //~ cnetsocket = netsocket_new(ccallback, "juzerdata"); + //~ netsocket_connect(netsocket, "pingoly", 5555); + //~ netsocket_destroy(netsocket); + + snetsocket = netsocket_new(scallback, "ez itt a szerver", EV_DEFAULT); + netsocket_lhost(snetsocket, lhost); + netsocket_lport(snetsocket, lport); + if (netsocket_listen(snetsocket)) { + perror("netsocket_listen"); + exit(255); + } + + ev_timer_init(&halfsec_timer, halfsec_timeout, 0.5, 0.5); + ev_timer_start(EV_DEFAULT, &halfsec_timer); + + // azonnali reconnect, majd disconnect esetén 1.5 másodperc múlva + //~ ev_timer_init(&reconnect_timer, reconnect_timeout, 0., 1.5); + //~ ev_timer_start(EV_DEFAULT, &reconnect_timer); + + ev_loop(EV_DEFAULT, 0); + + return 0; +} + diff --git a/ami_test_client/libc-jss/test.c b/ami_test_client/libc-jss/test.c new file mode 100644 index 0000000000..6827d7c69e --- /dev/null +++ b/ami_test_client/libc-jss/test.c @@ -0,0 +1,181 @@ +/* test.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "netsocket.h" + +static char *lhost = "0.0.0.0"; +static int lport = 1111; + +static char *host; +static int port; + +static int i = 0; +int n = 0; + +static netsocket_t *cnetsocket; // client +static netsocket_t *snetsocket; // server + +static ev_timer halfsec_timer; +static ev_timer reconnect_timer; + + +static void halfsec_timeout (EV_P_ ev_timer *w, int revents) { + //~ if (i == 2) netsocket_disconnect(netsocket, "ötödik másodperc miatt"); + //~ if (i == 7) netsocket_connect(netsocket, "pingoly", 5555); + //~ printf("timeout %d\n", n++); +} + +static void reconnect_timeout (EV_P_ ev_timer *w, int revents) { + ev_timer_stop(EV_DEFAULT, &reconnect_timer); + printf("Connecting [#%d] to %s:%d...\n", i++, host, port); + netsocket_connect(cnetsocket, host, port); +} + +static void ccallback (netsocket_t *netsocket, int event) { + //~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason); + + switch (event) { + case NETSOCKET_EVENT_CONNECT: + printf("Connected to %s (%s) port %d\n", + netsocket->host, + netsocket->ip, + netsocket->port + ); + char *msg = "Szevasz szerver!\n"; + netsocket_write(netsocket, msg, strlen(msg)); + break; + + case NETSOCKET_EVENT_DISCONNECT: + //~ ev_timer_again(EV_DEFAULT, &reconnect_timer); + ev_timer_start(EV_DEFAULT, &reconnect_timer); + if (netsocket->connected) { + printf("Disconnected from %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + } else { + printf("Can't connect to %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + } + break; + + case NETSOCKET_EVENT_READ: + printf("Got %d bytes from %s:%d\n", + netsocket->inbuf_len, + netsocket->host, + netsocket->port + ); + printf("inbuf = %s\n", netsocket->inbuf); + break; + + default: + printf("Unhandled event: %d\n", event); + break; + } + + //~ return; + //~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n", + //~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err)); +} + +static void scallback (netsocket_t *netsocket, int event) { + //~ printf("Callback event = %d - %s\n", event, netsocket->disconnect_reason); + + switch (event) { + case NETSOCKET_EVENT_CONNECT: + // a netsocket az új kliens socket + // a szülőt, tehát azt a netsocket objektumot, ami a szerver + // és figyel a porton, azt a netsocket->parent lehet elérni + // a netsocket->userdata megegyezik a szülő userdata-val + printf("Connect from %s:%d (%s)\n", + netsocket->ip, + netsocket->port, + netsocket->host + ); + char *msg = "Szevasz kliens!\n"; + netsocket_write(netsocket, msg, strlen(msg)); + //~ netsocket_disconnect(netsocket, "megszakadsz"); + //~ netsocket_destroy(netsocket); + break; + + case NETSOCKET_EVENT_DISCONNECT: + //~ ev_timer_again(EV_DEFAULT, &reconnect_timer); + //~ ev_timer_start(EV_DEFAULT, &reconnect_timer); + if (netsocket->connected) { + printf("Disconnected from %s: %s\n", + netsocket->host, + netsocket->disconnect_reason + ); + netsocket_destroy(netsocket); + } else { + printf("Can't listen on %s:%d: %s\n", + netsocket->host, + netsocket->port, + netsocket->disconnect_reason + ); + } + break; + + case NETSOCKET_EVENT_READ: + printf("Got %d bytes from %s:%d\n", + netsocket->inbuf_len, + netsocket->host, + netsocket->port + ); + printf("inbuf = %s\n", netsocket->inbuf); + break; + + default: + printf("Unhandled event: %d\n", event); + break; + } + + //~ return; + //~ printf("Callback jott: %s\nHost: %s\nIP: %s\nErr: %s\n", + //~ (char*)netsocket->userdata, netsocket->host, netsocket->ip, strerror(netsocket->err)); +} + +int main (int argc, char **argv) { + if (argc < 3) { + printf("usage: %s \n", argv[0]); + exit(255); + } + + host = argv[1]; + port = atoi(argv[2]); + + cnetsocket = netsocket_new(ccallback, "juzerdata"); + //~ netsocket_connect(netsocket, "pingoly", 5555); + //~ netsocket_destroy(netsocket); + + snetsocket = netsocket_new(scallback, "ez itt a szerver"); + if (netsocket_listen(snetsocket, lhost, lport)) { + perror("netsocket_listen"); + exit(255); + } + + ev_timer_init(&halfsec_timer, halfsec_timeout, 0.5, 0.5); + ev_timer_start(EV_DEFAULT, &halfsec_timer); + + // azonnali reconnect, majd disconnect esetén 1.5 másodperc múlva + ev_timer_init(&reconnect_timer, reconnect_timeout, 0., 1.5); + //~ ev_timer_start(EV_DEFAULT, &reconnect_timer); + + ev_loop(EV_DEFAULT, 0); + + return 0; +} + diff --git a/ami_test_client/libc-jss/tracer/Makefile b/ami_test_client/libc-jss/tracer/Makefile new file mode 100644 index 0000000000..b157c31481 --- /dev/null +++ b/ami_test_client/libc-jss/tracer/Makefile @@ -0,0 +1,11 @@ +CC = gcc +CFLAGS = -Wall -I.. -ggdb +LDFLAGS = -lpthread + +all: test_tracer + +test_tracer: test_tracer.o tracer.o + $(CC) $(CFLAGS) -o test_tracer test_tracer.o tracer.o $(LDFLAGS) + +clean: + rm -f test_tracer *.o diff --git a/ami_test_client/libc-jss/tracer/test_tracer.c b/ami_test_client/libc-jss/tracer/test_tracer.c new file mode 100644 index 0000000000..4b6e092bea --- /dev/null +++ b/ami_test_client/libc-jss/tracer/test_tracer.c @@ -0,0 +1,33 @@ +/* test_tracer.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include + +#include "tracer.h" +#include "debug.h" + +int main (int argc, const char *argv[]) { + tracer_t *tra = tracer_new("proba"); + if (tra == NULL) { + printf("out of memory\n"); + return -1; + } + + tracer_open(tra); + tracer_printf(tra, "hello %d\n", 59); + tracer_printf(tra, "hallo %d\n", 61); + printf("lezárva: %llu\n", tracer_close(tra)); + + tracer_destroy(tra); + return -1; +} + diff --git a/ami_test_client/libc-jss/tracer/tracer.c b/ami_test_client/libc-jss/tracer/tracer.c new file mode 100644 index 0000000000..01a9b3463b --- /dev/null +++ b/ami_test_client/libc-jss/tracer/tracer.c @@ -0,0 +1,185 @@ +/* tracer.c - simple trace file handling for debugging + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "tracer.h" + + +#define TRACER_DEFAULT_START_ID 10000 + + +// A tra->id -be rak egy új id-t, amit a tra->seqfile alapján inkrementál és +// ment el. Ha nem létezik a seq fájl vagy faszság van benne, akkor +// tra->start_id értéke kerül a tra->id -be, és ezt is menti el a seq fájlba. +// +// visszatérési érték: +// 0 = sikeres volt új id-t generálni és elmenteni +// -1 = valami grimbusz volt +static int new_id (tracer_t *tra) { + // ha nem létezik még a tra->seqpath fájl, akkor legyártjuk 644-es joggal, + // mert különben az fopen "r+" NULL-al tér vissza + if (access(tra->seqpath, F_OK)) { + int fd = open(tra->seqpath, O_RDWR | O_CREAT, 0644); + close(fd); + } + + FILE *file = fopen(tra->seqpath, "r+"); + if (file == NULL) { + printf("Can't open %s: %s\n", tra->seqpath, strerror(errno)); + return -1; + } + + // seq fájl zárolása + flock(fileno(file), LOCK_EX); + + unsigned long long id = 0; + fscanf(file, "%llu\n", &id); + id = (id == 0) ? tra->start_id : id + 1; + rewind(file); + fprintf(file, "%llu\n", id); + + // seq fájl elengedése + flock(fileno(file), LOCK_UN); + fclose(file); + + tra->id = id; + return 0; +} + +// visszatérési érték: +// 0 = létezik a könyvtár +// -1 = nem létezik a könyvtár +static int test_directory_exists (const char *path) { + DIR *dir = opendir(path); + if (dir) { + closedir(dir); + return 0; + } + return -1; +} + +// Létrehoz egy új tracer objektumot. Paraméterül kap egy könyvtár nevet, ahova +// a tracer fájlokat fogja menteni a tracer_open(), tracer_printf() és +// tracer_close(). A könyvtárnév lehet relatív elérési útvonal is. A +// tracer_new() a realpath() rendszerhívás segítségével megállapítja az +// abszolút útvonalat (/var/log/....) és a továbbiakban ezt fogja használni. +// Ezáltal a programban tetszés szerint lehet chdir() hívásokat elhelyezni. +// Azok nem fogják befolyásolni a tracert. +tracer_t *tracer_new (const char *directory) { + tracer_t *tra = malloc(sizeof(*tra)); + if (tra == NULL) { + fprintf(stderr, "tracer_new(): out of memory"); + return NULL; + } + memset(tra, 0, sizeof(*tra)); + + // relatív útvonalat menti el a tra->path -ba + realpath(directory, tra->path); + + // tra->path könyvtár készítése 755 jogokkal + mkdir(tra->path, 0755); + + // ha nem létezik a könyvtár, akkor grimbusz van és NULL-ra állítjuk a + // path-ot, aminek hatására a tracer_open() nem fog dolgozni + if (test_directory_exists(tra->path)) { + tra->path[0] = '\0'; + } + + // sorszám-fájl elérési útjának tárolása + snprintf(tra->seqpath, sizeof(tra->seqpath), "%s/.seq", tra->path); + + // default értékek + tra->start_id = TRACER_DEFAULT_START_ID; // ezt írja felül a tracer_set_start_id() + + return tra; +} + +void tracer_open (tracer_t *tra) { + if (tra == NULL || tra->path[0] == '\0') + return; + + // ha már van nyitott fájlunk, akkor azt először bezárjuk + if (tra->file != NULL) + tracer_close(tra); + + // új tra->id generálása seq fájlból. Ha nem sikerül, akkor return-al + // visszatérünk + if (new_id(tra)) + return; + + // fájl teljes elérési útjának elkészítése + snprintf(tra->filepath, sizeof(tra->filepath), "%s/%llu", tra->path, tra->id); + + // megnyitás írásra + tra->file = fopen(tra->filepath, "w"); +} + +unsigned long long tracer_close (tracer_t *tra) { + if (tra == NULL || tra->file == NULL) + return 0; + + fclose(tra->file); + tra->file = NULL; + unsigned long long tmp = tra->id; + tra->id = 0; + return tmp; +} + +void tracer_printf (tracer_t *tra, const char *fmt, ...) { + if (tra == NULL || tra->file == NULL) + return; + + va_list ap; + va_start(ap, fmt); + vfprintf(tra->file, fmt, ap); + va_end(ap); +} + + +void tracer_destroy (tracer_t *tra) { + if (tra == NULL) + return; + + tracer_close(tra); + free(tra); +} + +void tracer_set_start_id (tracer_t *tra, int start_id) { + if (tra == NULL) + return; + + tra->start_id = start_id; +} + +unsigned long long tracer_get_id (tracer_t *tra) { + if (tra == NULL) + return 0; + + return tra->id; +} + +char *tracer_get_id_as_string (tracer_t *tra) { + if (tra == NULL) + return 0; + + snprintf(tra->id_as_string, sizeof(tra->id_as_string), "%llu", tra->id); + return tra->id_as_string; +} + diff --git a/ami_test_client/libc-jss/tracer/tracer.h b/ami_test_client/libc-jss/tracer/tracer.h new file mode 100644 index 0000000000..7248d71a61 --- /dev/null +++ b/ami_test_client/libc-jss/tracer/tracer.h @@ -0,0 +1,110 @@ +/* tracer.h - simple trace file handling for debugging + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + SYNOPSIS + -------- + + tracer_t *tra = tracer_new("MyTraceLogFolder"); + if (tra == NULL) { + printf("out of memory\n"); + return -1; + } + + // first file + tracer_open(tra); + tracer_printf(tra, "blahhh\n"); + tracer_close(tra); + + // secondary file + tracer_open(tra); + tracer_printf(tra, "hello %d\n", 59); + tracer_printf(tra, "hallo %d\n", 61); + + // you can save this ID to your logs for later examination + unsigned long long id; + id = tracer_get_id(tra); + + // close and retrieve ID in one step + printf("closed, tracer id = %llu\n", tracer_close(tra)); + + // close and free all resources + tracer_destroy(tra); + + + DESCRIPTION + ----------- + + A tracer elve az, hogy a program (démon) futása során tetszőleges, + nagyobb mennyiségű információt gyűjthessünk össze debuggolás céljából + úgy, hogy az ne szemetelje tele a konzolt vagy a log fájlokat. Ezek a + debug infók sorszámozott szöveges fájlokba kerülnek, majd a fájlok + sorszámára lehet hivatkozni a logban, így utólag könnyen kikereshető egy + esemény részletes körülményképe. Ráadásul egy `find -mtime` paranccsal + gyorsan meg lehet szabadnulni a régi, fölösleges tracelogoktól. + +*/ + + +#include + + +typedef struct tracer_t { + unsigned long long id; + char id_as_string[32]; + unsigned long long start_id; + FILE *file; + char path[PATH_MAX]; // realpath() függvény ide rakja az abszolút elérési utat + char filepath[PATH_MAX]; // path + fájlnév kerül ide (tracer adat fájl) + char seqpath[PATH_MAX]; // path + fájlnév kerül ide (sorszám fájl) +} tracer_t; + + +// új tracer objektum, melynek munkakönyvtára a directory-ban megadott abszolút +// vagy relatív elérési útvonal. A tracer_new() a realpath() rendszerhívás +// segítségével megállapítja az abszolút útvonalat (/var/log/....) és a +// továbbiakban ezt fogja használni. Ezáltal a programban tetszés szerint lehet +// chdir() hívásokat elhelyezni, mert azok nem fogják befolyásolni a tracert. +tracer_t *tracer_new (const char *directory); + +// Megnyitja írásra a sorban következő új fájlt. A fájl sorszámát a +// munkakönyvtárban található .seq fájl alapján inkrementálja. Ha a .seq nem +// létezik, akkor a tracer_set_start_id() függvénnyel beállított érték lesz a +// kezdő sorszám. Ennek alapértelmezett értéke TRACER_DEFAULT_START_ID. Ha a +// tracer_new() nem tudja beállítani vagy létrehozni a munkakönyvtárat, +// akkor a tracer_open() nem csinál semmit. A .seq fájl kezelése szál-biztos és +// processz-biztos, mert flock() által van zárolva. +void tracer_open (tracer_t *tra); + +// Formázott szöveget ír a már megnyitott fájlba. Ha nincs megnyitva a fájl, +// akkor nem csinál semmit. +void tracer_printf (tracer_t *tra, const char *fmt, ...); + +// Lezárja a tracer fájlt és visszaadja a sorszámát, ugyan azt, amit a +// tracer_get_id() ad vissza. +unsigned long long tracer_close (tracer_t *tra); + +// Ha van, akkor lezárja az éppen megnyitott fájlt és felszabadítja a tracer +// objektum által lefoglalt memóriát. Ha a paraméterül NULL-t kap, nem csinál +// semmit. +void tracer_destroy (tracer_t *tra); + +// Beállítja a kezdő sorszámot, mely akkor lesz felhasználva, ha nincs .seq +// fájl. Az alapértelmezett érték TRACER_DEFAULT_START_ID. +void tracer_set_start_id (tracer_t *tra, int start_id); + +// Visszaadja az éppen használt fájl sorszámát. Ha a hívás pillanatában nincs +// nyitva fájl, tehát nem egy tracer_open() és egy tracer_close() között +// vagyunk, akkor a visszatérési érték "0". +unsigned long long tracer_get_id (tracer_t *tra); + +// String-ként adja vissza az éppen használt fájl sorszámát. Ha a hívás +// pillanatában nincs nyitva fájl, tehát nem egy tracer_open() és egy +// tracer_close() között vagyunk, akkor a visszatérési érték "0". +char *tracer_get_id_as_string (tracer_t *tra); + diff --git a/ami_test_client/libc-jss/udns/COPYING.LGPL b/ami_test_client/libc-jss/udns/COPYING.LGPL new file mode 100644 index 0000000000..4362b49151 --- /dev/null +++ b/ami_test_client/libc-jss/udns/COPYING.LGPL @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ami_test_client/libc-jss/udns/Makefile.in b/ami_test_client/libc-jss/udns/Makefile.in new file mode 100644 index 0000000000..ec08520665 --- /dev/null +++ b/ami_test_client/libc-jss/udns/Makefile.in @@ -0,0 +1,197 @@ +#! /usr/bin/make -rf +# Makefile.in +# libudns Makefile +# +# Copyright (C) 2005 Michael Tokarev +# This file is part of UDNS library, an async DNS stub resolver. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library, in file named COPYING.LGPL; if not, +# write to the Free Software Foundation, Inc., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +NAME = udns +VERS = 0.4 +SOVER = 0 + +SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ + udns_misc.c udns_XtoX.c \ + udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ + udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c +USRCS = dnsget.c rblcheck.c ex-rdns.c +DIST = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \ + NEWS TODO NOTES Makefile.in configure configure.lib \ + inet_XtoX.c getopt.c + +OBJS = $(SRCS:.c=.o) $(GEN:.c=.o) +LIB = lib$(NAME).a +LIBFL = -L. -l$(NAME) + +SOBJS = $(OBJS:.o=.lo) +SOLIB = lib$(NAME)_s.so +SOLIBV = lib$(NAME).so.$(SOVER) +SOLIBFL= -L. -l$(NAME)_s + +UTILS = $(USRCS:.c=) +UOBJS = $(USRCS:.c=.o) +SOUTILS = $(USRCS:.c=_s) + +NAMEPFX = $(NAME)-$(VERS) + +CC = @CC@ +CFLAGS = @CFLAGS@ +CDEFS = @CDEFS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +LDSHARED = $(LD) -shared +PICFLAGS = -fPIC +AWK = awk +TAR = tar + +all: static + +.SUFFIXES: .c .o .lo + +static: $(LIB) $(UTILS) +staticlib: $(LIB) +$(LIB): $(OBJS) + -rm -f $@ + $(AR) rv $@ $(OBJS) +.c.o: + $(CC) $(CFLAGS) $(CDEFS) -c $< + +shared: $(SOLIBV) $(SOUTILS) +sharedlib: $(SOLIBV) + +$(SOLIBV): $(SOBJS) + $(LDSHARED) -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS) $(LDFLAGS) $(LIBS) +$(SOLIB): $(SOLIBV) + rm -f $@ + ln -s $(SOLIBV) $@ +.c.lo: + $(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $< + +# udns_codes.c is generated from udns.h +udns_codes.c: udns.h + @echo Generating $@ + @set -e; exec >$@.tmp; \ + set T type C class R rcode; \ + echo "/* Automatically generated. */"; \ + echo "#include \"udns.h\""; \ + while [ "$$1" ]; do \ + echo; \ + echo "const struct dns_nameval dns_$${2}tab[] = {"; \ + $(AWK) "/^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + { printf \" {%s,\\\"%s\\\"},\\n\", \$$1, substr(\$$1,7) }" \ + udns.h ; \ + echo " {0,0}};"; \ + echo "const char *dns_$${2}name(enum dns_$${2} code) {"; \ + echo " static char nm[20];"; \ + echo " switch(code) {"; \ + $(AWK) "BEGIN{i=0} \ + /^ DNS_$${1}_[A-Z0-9_]+[ ]*=/ \ + {printf \" case %s: return dns_$${2}tab[%d].name;\\n\",\$$1,i++}\ + " udns.h ; \ + echo " }"; \ + echo " return _dns_format_code(nm,\"$$2\",code);"; \ + echo "}"; \ + shift 2; \ + done + @mv $@.tmp $@ + +udns.3.html: udns.3 + groff -man -Thtml udns.3 > $@.tmp + mv $@.tmp $@ + +dist: $(NAMEPFX).tar.gz +$(NAMEPFX).tar.gz: $(DIST) + $(TAR) -cv -f $@ -z --transform 's|^|$(NAMEPFX)/|' $(DIST) + +subdist: + cp -p $(DIST) $(TARGET)/ + +clean: + rm -f $(OBJS) + rm -f $(SOBJS) + rm -f $(UOBJS) + rm -f config.log +distclean: clean + rm -f $(LIB) $(SOLIB) $(SOLIBV) udns.3.html + rm -f $(UTILS) $(SOUTILS) + rm -f config.status config.h Makefile + + +Makefile: configure configure.lib Makefile.in + ./configure + @echo + @echo Please rerun make >&2 + @exit 1 + +.PHONY: all static staticlib shared sharedlib dist clean distclean subdist \ + depend dep deps + +depend dep deps: $(SRCS) $(USRC) + @echo Generating deps for: + @echo \ $(SRCS) + @echo \ $(USRCS) + @sed '/^# depend/q' Makefile.in > Makefile.tmp + @set -e; \ + for f in $(SRCS) $(USRCS); do \ + echo $${f%.c}.o $${f%.c}.lo: $$f \ + `sed -n 's/^#[ ]*include[ ]*"\(.*\)".*/\1/p' $$f`; \ + done >> Makefile.tmp; \ + for f in $(USRCS:.c=.o); do \ + echo "$${f%.?}: $$f \$$(LIB)"; \ + echo " \$$(LD) \$$(LDLAGS) -o \$$@ $$f \$$(LIBFL) \$$(LIBS)"; \ + echo "$${f%.?}_s: $$f \$$(SOLIB)"; \ + echo " \$$(LD) \$$(LDFLAGS) -o \$$@ $$f \$$(SOLIBFL)"; \ + done >> Makefile.tmp ; \ + if cmp Makefile.tmp Makefile.in >/dev/null 2>&1 ; then \ + echo Makefile.in unchanged; rm -f Makefile.tmp; \ + else \ + echo Updating Makfile.in; mv -f Makefile.tmp Makefile.in ; \ + fi + +# depend +udns_dn.o udns_dn.lo: udns_dn.c udns.h +udns_dntosp.o udns_dntosp.lo: udns_dntosp.c udns.h +udns_parse.o udns_parse.lo: udns_parse.c udns.h +udns_resolver.o udns_resolver.lo: udns_resolver.c config.h udns.h +udns_init.o udns_init.lo: udns_init.c config.h udns.h +udns_misc.o udns_misc.lo: udns_misc.c udns.h +udns_XtoX.o udns_XtoX.lo: udns_XtoX.c config.h udns.h inet_XtoX.c +udns_rr_a.o udns_rr_a.lo: udns_rr_a.c udns.h +udns_rr_ptr.o udns_rr_ptr.lo: udns_rr_ptr.c udns.h +udns_rr_mx.o udns_rr_mx.lo: udns_rr_mx.c udns.h +udns_rr_txt.o udns_rr_txt.lo: udns_rr_txt.c udns.h +udns_bl.o udns_bl.lo: udns_bl.c udns.h +udns_rr_srv.o udns_rr_srv.lo: udns_rr_srv.c udns.h +udns_rr_naptr.o udns_rr_naptr.lo: udns_rr_naptr.c udns.h +udns_codes.o udns_codes.lo: udns_codes.c udns.h +udns_jran.o udns_jran.lo: udns_jran.c udns.h +dnsget.o dnsget.lo: dnsget.c config.h udns.h getopt.c +rblcheck.o rblcheck.lo: rblcheck.c config.h udns.h getopt.c +ex-rdns.o ex-rdns.lo: ex-rdns.c udns.h +dnsget: dnsget.o $(LIB) + $(LD) $(LDLAGS) -o $@ dnsget.o $(LIBFL) $(LIBS) +dnsget_s: dnsget.o $(SOLIB) + $(LD) $(LDFLAGS) -o $@ dnsget.o $(SOLIBFL) +rblcheck: rblcheck.o $(LIB) + $(LD) $(LDLAGS) -o $@ rblcheck.o $(LIBFL) $(LIBS) +rblcheck_s: rblcheck.o $(SOLIB) + $(LD) $(LDFLAGS) -o $@ rblcheck.o $(SOLIBFL) +ex-rdns: ex-rdns.o $(LIB) + $(LD) $(LDLAGS) -o $@ ex-rdns.o $(LIBFL) $(LIBS) +ex-rdns_s: ex-rdns.o $(SOLIB) + $(LD) $(LDFLAGS) -o $@ ex-rdns.o $(SOLIBFL) diff --git a/ami_test_client/libc-jss/udns/NEWS b/ami_test_client/libc-jss/udns/NEWS new file mode 100644 index 0000000000..88aff6fa42 --- /dev/null +++ b/ami_test_client/libc-jss/udns/NEWS @@ -0,0 +1,136 @@ +NEWS +User-visible changes in udns library. Recent changes on top. + +0.4 (Jan 2014) + + - bugfix: fix a bug in new list code introduced in 0.3 + - portability: use $(LD)/$(LDFLAGS)/$(LIBS) + +0.3 (Jan 2014) + + - bugfix: refactor double-linked list implementation in udns_resolver.c + (internal to the library) to be more strict-aliasing-friendly, because + old code were miscompiled by gcc. + + - bugfix: forgotten strdup() in rblcheck + +0.2 (Dec 2011) + + - bugfix: SRV RR handling: fix domain name parsing and crash in case + if no port is specified on input for SRV record query + + - (trivial api) dns_set_opts() now returns number of unrecognized + options instead of always returning 0 + + - dnsget: combine -f and -o options in dnsget (and stop documenting -f), + and report unknown/invalid -o options (and error out) + + - dnsget: pretty-print SSHFP RRs + + 0.1 (Dec 2010) + + - bugfix: udns_new(old) - when actually cloning another context - + makes the new context referencing memory from old, which leads + to crashes when old is modified later + + - use random queue IDs (the 16bit qID) in queries instead of sequentional + ones, based on simple pseudo-random RNG by Bob Jenkins (udns_jran.[ch]). + Some people believe that this improves security (CVE-2008-1447). I'm + still not convinced (see comments in udns_resolver.c), but it isn't + difficult to add after all. + + - deprecate dns_random16() function which was declared in udns.h + (not anymore) but never documented. In order to keep ABI compatible + it is still exported. + + - library has a way now to set query flags (DNS_SET_DO; DNS_SET_CD). + + - dnsget now prints non-printable chars in all strings in DNS RRs using + decimal escape sequences (\%03u) instead of hexadecimal (\%02x) when + before - other DNS software does it like this. + + - recognize a few more record types in dnsget, notable some DNSSEC RRs; + add -f option for dnsget to set query flags. + + - udns is not a Debian native package anymore (was a wrong idea) + +0.0.9 (16 Jan 2007) + + - incompat: minor API changes in dns_init() &friends. dns_init() + now requires extra `struct dns_ctx *' argument. Not bumped + soversion yet - I only expect one "release" with this change. + + - many small bugfixes, here and there + + - more robust FORMERR replies handling - not only such replies are now + recognized, but udns retries queries without EDNS0 extensions if tried + with, but server reported FORMERR + + - portability changes, udns now includes getopt() implementation fo + the systems lacking it (mostly windows), and dns_ntop()&dns_pton(), + which are either just wrappers for system functions or reimplementations. + + - build is now based on autoconf-like configuration + + - NAPTR (RFC3403) RR decoding support + + - new file NOTES which complements TODO somewhat, and includes some + important shortcomings + + - many internal cleanups, including some preparations for better error + recovery, security and robustness (and thus API changes) + + - removed some #defines which are now unused (like DNS_MAXSRCH) + + - changed WIN32 to WINDOWS everywhere in preprocessor tests, + to be able to build it on win64 as well + +0.0.8 (12 Sep 2005) + + - added SRV records (rfc2782) parsing, + thanks to Thadeu Lima de Souza Cascardo for implementation. + + - bugfixes: + o use uninitialized value when no reply, library died with assertion: + assert((status < 0 && result == 0) || (status >= 0 && result != 0)). + o on some OSes, struct sockaddr_in has additional fields, so + memcmp'ing two sockaddresses does not work. + + - rblcheck(.1) + +0.0.7 (20 Apr 2005) + + - dnsget.1 manpage and several enhancements to dnsget. + + - allow nameserver names for -n option of dnsget. + + - API change: all dns_submit*() routines now does not expect + last `now' argument, since requests aren't sent immediately + anymore. + + - API change: different application timer callback mechanism. + Udns now uses single per-context timer instead of per-query. + + - don't assume DNS replies only contain backward DN pointers, + allow forward pointers too. Change parsing API. + + - debianize + +0.0.6 (08 Apr 2005) + + - use double sorted list for requests (sorted by deadline). + This should significantly speed up timeout processing for + large number of requests. + + - changed debugging interface, so it is finally useable + (still not documented). + + - dnsget routine is now Officially Useable, and sometimes + even more useable than `host' from BIND distribution + (and sometimes not - dnsget does not have -C option + and TCP mode) + + - Debian packaging in debian/ -- udns is now maintained as a + native Debian package. + + - alot (and I really mean alot) of code cleanups all over. diff --git a/ami_test_client/libc-jss/udns/NOTES b/ami_test_client/libc-jss/udns/NOTES new file mode 100644 index 0000000000..b99a077bb2 --- /dev/null +++ b/ami_test_client/libc-jss/udns/NOTES @@ -0,0 +1,226 @@ +Assorted notes about udns (library). + +UDP-only mode +~~~~~~~~~~~~~ + +First of all, since udns is (currently) UDP-only, there are some +shortcomings. + +It assumes that a reply will fit into a UDP buffer. With adoption of EDNS0, +and general robustness of IP stacks, in most cases it's not an issue. But +in some cases there may be problems: + + - if an RRset is "very large" so it does not fit even in buffer of size + requested by the library (current default is 4096; some servers limits + it further), we will not see the reply, or will only see "damaged" + reply (depending on the server). + + - many DNS servers ignores EDNS0 option requests. In this case, no matter + which buffer size udns library will request, such servers reply is limited + to 512 bytes (standard pre-EDNS0 DNS packet size). (Udns falls back to + non-EDNO0 query if EDNS0-enabled one received FORMERR or NOTIMPL error). + +The problem is that with this, udns currently will not consider replies with +TC (truncation) bit set, and will treat such replies the same way as it +treats SERVFAIL replies, thus trying next server, or temp-failing the query +if no more servers to try. In other words, if the reply is really large, or +if the servers you're using don't support EDNS0, your application will be +unable to resolve a given name. + +Yet it's not common situation - in practice, it's very rare. + +Implementing TCP mode isn't difficult, but it complicates API significantly. +Currently udns uses only single UDP socket (or - maybe in the future - two, +see below), but in case of TCP, it will need to open and close sockets for +TCP connections left and right, and that have to be integrated into an +application's event loop in an easy and efficient way. Plus all the +timeouts - different for connect(), write, and several stages of read. + +IPv6 vs IPv4 usage +~~~~~~~~~~~~~~~~~~ + +This is only relevant for nameservers reachable over IPv6, NOT for IPv6 +queries. I.e., if you've IPv6 addresses in 'nameservers' line in your +/etc/resolv.conf file. Even more: if you have BOTH IPv6 AND IPv4 addresses +there. Or pass them to udns initialization routines. + +Since udns uses a single UDP socket to communicate with all nameservers, +it should support both v4 and v6 communications. Most current platforms +supports this mode - using PF_INET6 socket and V4MAPPED addresses, i.e, +"tunnelling" IPv4 inside IPv6. But not all systems supports this. And +more, it has been said that such mode is deprecated. + +So, list only IPv4 or only IPv6 addresses, but don't mix them, in your +/etc/resolv.conf. + +An alternative is to use two sockets instead of 1 - one for IPv6 and one +for IPv4. For now I'm not sure if it's worth the complexity - again, of +the API, not the library itself (but this will not simplify library either). + +Single socket for all queries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using single UDP socket for sending queries to all nameservers has obvious +advantages. First it's, again, trivial, simple to use API. And simple +library too. Also, after sending queries to all nameservers (in case first +didn't reply in time), we will be able to receive late reply from first +nameserver and accept it. + +But this mode has disadvantages too. Most important is that it's much easier +to send fake reply to us, as the UDP port where we expects the reply to come +to is constant during the whole lifetime of an application. More secure +implementations uses random port for every single query. While port number +(16 bits integer) can not hold much randomness, it's still of some help. +Ok, udns is a stub resolver, so it expects sorta friendly environment, but +on LAN it's usually much easier to fire an attack, due to the speed of local +network, where a bad guy can generate alot of packets in a short time. + +Spoofing of replies (Kaminsky attack, CVE-2008-1447) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While udns uses random numbers for query IDs, it uses single UDP port for +all queries (see previous item). And even if it used random UDP port for +each query, the attack described in CVE-2008-1447 is still quite trivial. +This is not specific to udns library unfortunately - it is inherent property +of the protocol. Udns is designed to work in a LAN, it needs full recursive +resolver nearby, and modern LAN usually uses high-bandwidth equipment which +makes the Kaminsky attack trivial. The problem is that even with qID (16 +bits) and random UDP port (about 20 bits available to a regular process) +combined still can not hold enough randomness, so on a fast network it is +still easy to flood the target with fake replies and hit the "right" reply +before real reply comes. So random qIDs don't add much protection anyway, +even if this feature is implemented in udns, and using all available +techniques wont solve it either. + +See also long comment in udns_resolver.c, udns_newid(). + +Assumptions about RRs returned +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Currently udns processes records in the reply it received sequentially. +This means that order of the records is significant. For example, if +we asked for foo.bar A, but the server returned that foo.bar is a CNAME +(alias) for bar.baz, and bar.baz, in turn, has address 1.2.3.4, when +the CNAME should come first in reply, followed by A. While DNS specs +does not say anything about order of records - it's an rrSET - unordered, - +I think an implementation which returns the records in "wrong" order is +somewhat insane... + +CNAME recursion +~~~~~~~~~~~~~~~ + +Another interesting point is the handling of CNAMEs returned as replies +to non-CNAME queries. If we asked for foo.bar A, but it's a CNAME, udns +expects BOTH the CNAME itself and the target DN to be present in the reply. +In other words, udns DOES NOT RECURSE CNAMES. If we asked for foo.bar A, +but only record in reply was that foo.bar is a CNAME for bar.baz, udns will +return no records to an application (NXDOMAIN). Strictly speaking, udns +should repeat the query asking for bar.baz A, and recurse. But since it's +stub resolver, recursive resolver should recurse for us instead. + +It's not very difficult to implement, however. Probably with some (global?) +flag to en/dis-able the feature. Provided there's some demand for it. + +To clarify: udns handles CNAME recursion in a single reply packet just fine. + +Note also that standard gethostbyname() routine does not recurse in this +situation, too. + +Error reporting +~~~~~~~~~~~~~~~ + +Too many places in the code (various failure paths) sets generic "TEMPFAIL" +error condition. For example, if no nameserver replied to our query, an +application will get generic TEMPFAIL, instead of something like TIMEDOUT. +This probably should be fixed, but most applications don't care about the +exact reasons of failure - 4 common cases are already too much: + - query returned some valid data + - NXDOMAIN + - valid domain but no data of requested type - =NXDOMAIN in most cases + - temporary error - this one sometimes (incorrectly!) treated as NXDOMAIN + by (naive) applications. +DNS isn't yes/no, it's at least 3 variants, temp err being the 3rd important +case! And adding more variations for the temp error case is complicating things +even more - again, from an application writer standpoint. For diagnostics, +such more specific error cases are of good help. + +Planned API changes +~~~~~~~~~~~~~~~~~~~ + +At least one thing I want to change for some future version is a way how +queries are submitted and how replies are handled. + +I want to made dns_query object to be owned by an application. So that instead +of udns library allocating it for the lifetime of query, it will be pre- +allocated by an application. This simplifies and enhances query submitting +interface, and complicates it a bit too, in simplest cases. + +Currently, we have: + +dns_submit_dn(dn, cls, typ, flags, parse, cbck, data) +dns_submit_p(name, cls, typ, flags, parse, cbck, data) +dns_submit_a4(ctx, name, flags, cbck, data) + +and so on -- with many parameters missed for type-specific cases, but generic +cases being too complex for most common usage. + +Instead, with dns_query being owned by an app, we will be able to separately +set up various parts of the query - domain name (various forms), type&class, +parser, flags, callback... and even change them at runtime. And we will also +be able to reuse query structures, instead of allocating/freeing them every +time. So the whole thing will look something like: + + q = dns_alloc_query(); + dns_submit(dns_q_flags(dns_q_a4(q, name, cbck), DNS_F_NOSRCH), data); + +The idea is to have a set of functions accepting struct dns_query* and +returning it (so the calls can be "nested" like the above), to set up +relevant parts of the query - specific type of callback, conversion from +(type-specific) query parameters into a domain name (this is for type- +specific query initializers), and setting various flags and options and +type&class things. + +One example where this is almost essential - if we want to support +per-query set of nameservers (which isn't at all useless: imagine a +high-volume mail server, were we want to direct DNSBL queries to a separate +set of nameservers, and rDNS queries to their own set and so on). Adding +another argument (set of nameservers to use) to EVERY query submitting +routine is.. insane. Especially since in 99% cases it will be set to +default NULL. But with such "nesting" of query initializers, it becomes +trivial. + +This change (the way how queries gets submitted) will NOT break API/ABI +compatibility with old versions, since the new submitting API works in +parallel with current (and current will use the new one as building +blocks, instead of doing all work at once). + +Another way to do the same is to manipulate query object right after a +query has been submitted, but before any events processing (during this +time, query object is allocated and initialized, but no actual network +packets were sent - it will happen on the next event processing). But +this way it become impossible to perform syncronous resolver calls, since +those calls hide query objects they use internally. + +Speaking of replies handling - the planned change is to stop using dynamic +memory (malloc) inside the library. That is, instead of allocating a buffer +for a reply dynamically in a parsing routine (or memdup'ing the raw reply +packet if no parsing routine is specified), I want udns to return the packet +buffer it uses internally, and change parsing routines to expect a buffer +for result. When parsing, a routine will return true amount of memory it +will need to place the result, regardless of whenever it has enough room +or not, so that an application can (re)allocate properly sized buffer and +call a parsing routine again. + +This, in theory, also can be done without breaking current API/ABI, but in +that case we'll again need a parallel set of routines (parsing included), +which makes the library more complicated with too many ways of doing the +same thing. Still, code reuse is at good level. + +Another modification I plan to include is to have an ability to work in +terms of domain names (DNs) as used with on-wire DNS packets, not only +with asciiz representations of them. For this to work, the above two +changes (query submission and result passing) have to be completed first +(esp. the query submission part), so that it will be possible to specify +some additional query flags (for example) to request domain names instead +of the text strings, and to allow easy query submissions with either DNs +or text strings. diff --git a/ami_test_client/libc-jss/udns/TODO b/ami_test_client/libc-jss/udns/TODO new file mode 100644 index 0000000000..0dc9b967c6 --- /dev/null +++ b/ami_test_client/libc-jss/udns/TODO @@ -0,0 +1,59 @@ +TODO + +The following is mostly an internal, not user-visible stuff. + +* rearrange an API to make dns_query object owned by application, + so that it'll look like this: + struct dns_query *q; + q = dns_query_alloc(ctx); + dns_query_set(q, options, domain_name, flags, ...); + dns_query_submit(ctx, q); + For more information see NOTES file, section "Planned API changes". + +* allow NULL callbacks? Or provide separate resolver + context list of queries which are done but wich did not + have callback, and dns_pick() routine to retrieve results + from this query, i.e. allow non-callback usage? The + non-callback usage may be handy sometimes (any *good* + example?), but it will be difficult to provide type-safe + non-callback interface due to various RR-specific types + in use. + +* DNS_OPT_FLAGS should be DNS_OPT_ADDFLAGS and DNS_OPT_SETFLAGS. + Currently one can't add a single flag bit but preserve + existing bits... at least not without retrieving all current + flags before, which isn't that bad anyway. + +* dns_set_opts() may process flags too (such as aaonly etc) + +* a way to disable $NSCACHEIP et al processing? + (with now separate dns_init() and dns_reset(), it has finer + control, but still no way to init from system files but ignore + environment variables and the like) + +* initialize/open the context automatically, and be more + liberal about initialization in general? + +* dns_init(ctx, do_open) - make the parameter opposite, aka + dns_init(ctx, skip_open) ? + +* allow TCP queue? + +* more accurate error reporting. Currently, udns always returns TEMPFAIL, + but don't specify why it happened (ENOMEM, timeout, etc). + +* check the error value returned by recvfrom() and + sendto() and determine which errors to ignore. + +* maybe merge dns_timeouts() and dns_ioevent(), to have + only one entry point for everything? For traditional + select-loop-based eventloop it may be easier, but for + callback-driven event loops the two should be separate. + Provide an option, or a single dns_events() entry point + for select-loop approach, or just call dns_ioevent() + from within dns_timeouts() (probably after renaming + it to be dns_events()) ? + +* implement /etc/hosts lookup too, ala [c-]ares?? + +* sortlist support? diff --git a/ami_test_client/libc-jss/udns/configure.lib b/ami_test_client/libc-jss/udns/configure.lib new file mode 100644 index 0000000000..541177a095 --- /dev/null +++ b/ami_test_client/libc-jss/udns/configure.lib @@ -0,0 +1,268 @@ +# configure.lib +# a library of shell routines for simple autoconf system +# + +set -e +ac_substitutes= +rm -f conftest* config.log +exec 5>config.log +cat <&5 +This file contains any messages produced by compilers etc while +running configure, to aid debugging if configure script makes a mistake. + +EOF + +case `echo "a\c"` in + *c*) ac_en=-n ac_ec= ;; + *) ac_en= ac_ec='\c' ;; +esac + +##### Messages +ac_msg() { + echo $ac_en "$*... $ac_ec" + echo ">>> $*" >&5 +} +ac_checking() { + echo $ac_en "checking $*... $ac_ec" + echo ">>> checking $*" >&5 +} +ac_result() { + echo "$1" + echo "=== $1" >&5 +} +ac_fatal() { + echo "configure: fatal: $*" >&2 + echo "=== FATAL: $*" >&5 + exit 1 +} +ac_warning() { + echo "configure: warning: $*" >&2 + echo "=== WARNING: $*" >&5 +} +ac_ign() { + "$@" || : +} + +# ac_run command... +# captures output in conftest.out +ac_run() { + # apparently UnixWare (for one) /bin/sh optimizes the following "if" + # "away", by checking if there's such a command BEFORE redirecting + # output. So error message (like "gcc: command not found") goes + # to stderr instead of to conftest.out, and `cat conftest.out' below + # fails. + if "$@" >conftest.out 2>&1; then + return 0 + else + echo "==== Command invocation failed. Command line was:" >&5 + echo "$*" >&5 + echo "==== compiler input was:" >&5 + cat conftest.c >&5 + echo "==== output was:" >&5 + cat conftest.out >&5 + echo "====" >&5 + return 1 + fi +} + +# common case for ac_verbose: yes/no result +ac_yesno() { + ac_checking "$1" + shift + if "$@"; then + ac_result yes + return 0 + else + ac_result no + return 1 + fi +} + +ac_subst() { + ac_substitutes="$ac_substitutes $*" +} + +ac_define() { + CDEFS="$CDEFS -D$1=${2:-1}" +} + +ac_have() { + ac_what=$1; shift + if "$@"; then + ac_define HAVE_$ac_what + eval ac_have_$ac_what=yes + return 0 + else + eval ac_have_$ac_what=no + return 1 + fi +} + +##### Compiling, linking + +# run a compiler +ac_run_compiler() { + rm -f conftest*; cat >conftest.c + ac_run $CC $CFLAGS $CDEFS "$@" conftest.c +} + +ac_compile() { + ac_run_compiler -c +} + +ac_link() { + ac_run_compiler -o conftest $LIBS "$@" +} + +ac_cpp() { + ac_run_compiler -E "$@" +} + +### check for C compiler. Set $CC, $CFLAGS etc +ac_prog_c_compiler_v() { + ac_checking "for C compiler" + rm -f conftest* + echo 'int main(int argc, char **argv) { return 0; }' >conftest.c + + if [ -n "$CC" ]; then + if ac_run $CC -o conftest conftest.c && ac_run ./conftest; then + ac_result "\$CC ($CC)" + else + ac_result no + ac_fatal "\$CC ($CC) is not a working compiler" + fi + else + for cc in gcc cc ; do + if ac_run $cc -o conftest conftest.c && ac_run ./conftest; then + ac_result "$cc" + CC=$cc + break + fi + done + if [ -z "$CC" ]; then + ac_result no + ac_fatal "no working C compiler found in \$PATH. please set \$CC variable" + fi + fi + if [ -z "$CFLAGS" ]; then + if ac_yesno "whenever C compiler ($CC) is GNU CC" \ + ac_grep_cpp yEs_mAsTeR <conftest.c + for lib in "$@"; do + if ac_run $CC $CFLAGS $LDFLAGS conftest.c -o conftest $LIBS $lib; then + found=y + break + fi + done + if [ ! "$found" ]; then + ac_result "not found" + return 1 + fi + if [ -z "$lib" ]; then + ac_result "ok (none needed)" + else + ac_result "ok ($lib)" + LIBS="$LIBS $lib" + fi +} + +ac_compile_run() { + ac_link "$@" && ac_run ./conftest +} + +ac_grep_cpp() { + pattern="$1"; shift + ac_cpp "$@" && grep "$pattern" conftest.out >/dev/null +} + +ac_output() { + for var in $ac_substitutes; do + eval echo "\"s|@$var@|\$$var|\"" + done >conftest.sed + for file in "$@"; do + ac_msg "creating $file" + if [ -f $file.in ]; then + sed -f conftest.sed $file.in > $file.tmp + mv -f $file.tmp $file + ac_result ok + else + ac_result failed + ac_fatal "$file.in not found" + fi + done + rm -f conftest* +} + +ac_config_h() { + h=${1:-config.h} + ac_msg "creating $h" + rm -f $1.tmp + echo "/* automatically generated by configure. */" > $h.tmp + echo "$CDEFS" | tr ' ' ' +' | sed -e 's/^-D/#define /' -e 's/=/ /' >> $h.tmp + if [ -f $h ] && cmp -s $h.tmp $h ; then + rm -f $h.tmp + ac_result unchanged + else + mv -f $h.tmp $h + ac_result ok + fi + CDEFS=-DHAVE_CONFIG_H +} diff --git a/ami_test_client/libc-jss/udns/dnsget.1 b/ami_test_client/libc-jss/udns/dnsget.1 new file mode 100644 index 0000000000..200557fe94 --- /dev/null +++ b/ami_test_client/libc-jss/udns/dnsget.1 @@ -0,0 +1,195 @@ +.\" dnsget.1: dnsget manpage +.\" +.\" Copyright (C) 2005-2014 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH dnsget 1 "Jan 2014" "User Utilities" + +.SH NAME +dnsget \- DNS lookup utility + +.SH SYNOPSYS +.B dnsget +.RB [\| \-v \||\| \-q \|] +.RB [\| \-c +.IR class \|] +.RB [\| \-t +.IR type \|] +.RB [\| \-o +.IR opt , opt ,...] +.IR name \|.\|.\|. + +.SH DESCRIPTION +.B dnsget +is a simple command-line to perform DNS lookups, similar to +.BR host (1) +and +.BR dig (1). +It is useable for both interactive/debugging scenarious and +in scripts. +The program is implemented using +.BR udns (3) +library. + +.PP +By default, +.B dnsget +produces a human-readable output, similar to +.RS +.nf +alias.example.com. CNAME www.example.com. +www.example.com. A 192.168.1.1 +www.example.com. MX 10 mx.example.com. +.fi +.RE +which is just sufficient to see how a given name resolves. +Output format is controllable with +.B \-v +and +.B \-q +options -- the former increases verbosity level up to printing +the whole DNS contents of all packets sent and received, which +is suitable for debugging DNS problems, while the latter reduces +the level, making output more quiet, up to bare result with no +error messages, which is good for scripts. + +.SH OPTIONS + +The following options are recognized by +.BR dnsget : + +.TP +.B \-v +produce more detailed output. More +.BR \-v 's +means more details will be produced. With single +.BR \-v , dnsget +will print contents of all received DNS packets (in a readable format), +while with +.BR \-vv , +it will output all outgoing DNS packets too. + +.TP +.B \-q +the opposite for \fB\-v\fR -- produce less detailed output. +With single +.BR \-q , dnsget +will only show (decoded) data from final DNS resource records (RR), +while +.B \-qq +also suppresses error messages. + +.TP +\fB\-t \fItype\fR +request record(s) of the given type \fItype\fR. By default, +.B dnsget +will ask for IPv4 address (A) record, or for PTR record if the +argument in question is an IPv4 or IPv6 address. Recognized +types include A, AAAA, MX, TXT, CNAME, PTR, NS, SOA, ANY and +others. + +.TP +\fB\-c \fIclass\fR +request DNS record(s) of the given class \fIclass\fR. By +default +.B dnsget +uses IN class. Valid classes include IN, CH, HS, ANY. + +.TP +.B \-a +(compatibility option). Equivalent to setting query type to +.B ANY +and increasing verbosity level +.RB ( \-v ). + +.TP +.B \-C +(planned) + +.TP +.B \-x +(planned) + +.TP +\fB\-o \fIopt\fR,\fIopt\fR,... +(may be specified several times). +Set resolver options (in a form \fIoption\fR:\fIvalue\fR) as if they +were set in +.RB $ RES_OPTIONS +environment variable, or set query flags: +.RS +.TP +\fBtimeout\fR:\fIsec\fR +Set initial query timeout to \fIsec\fR. +.TP +\fBattempts\fR:\fInum\fR +(re)try every query \fInum\fR times before failing. +.TP +\fBudpbuf\fR:\fIbytes\fR +set DNS UDP buffer size to \fIbytes\fR bytes. Valid values +are from 512 to 65535. If \fIbytes\fR is greather than 512, +EDNS0 (RFC 2671) extensions will be used. +.TP +\fBport\fR:\fInum\fR +Use given UDP port number \fInum\fR instead of the default port 53 (domain). +.TP +\fBaa\fR +set AA (auth only) query bit. +.TP +\fBnord\fR +do not set RD (recursion desired) query bit (set by default). +.TP +\fBdnssec\fR or \fBdo\fR +set DNSSEC OK (DO) query flag (\fBdnsget\fR does not verify DNSSEC signatures, +only displays them; this is set in EDNS RR). +.TP +\fBcd\fR +set CD (checking disabled) query bit. +.RE + +.TP +\fB\-n \fInameserver\fR +Use the given nameserver(s) (may be specified more than once) +instead of the default. Using this option has the same same effect as +.RB $ NSCACHEIP +or +.RB $ NAMESERVERS +environment variables, with the only difference that only IPv4 addresses +are recognized for now, and it is possible to specify names (which will +be resolved using default settings) instead of IP addresses. + +.TP +.B \-h +print short help and exit. + +.SH "RETURN VALUE" +When all names where resovled successefully, +.B dnsget +exits with zero exit status. If at least one name was not found, +.B dnsget +will exit with return code 100. If some other error occured during +name resolution, it will exit with code 99. In case of usage or +initialization error, +.B dnsget +will return 1. + +.SH "SEE ALSO" +.BR host (1) +.BR dig (1) +.BR resolv.conf (5) +.BR udns (3). diff --git a/ami_test_client/libc-jss/udns/dnsget.c b/ami_test_client/libc-jss/udns/dnsget.c new file mode 100644 index 0000000000..417e8d9743 --- /dev/null +++ b/ami_test_client/libc-jss/udns/dnsget.c @@ -0,0 +1,759 @@ +/* dnsget.c + simple host/dig-like application using UDNS library + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "udns.h" + +#ifndef HAVE_GETOPT +# include "getopt.c" +#endif + +#ifndef AF_INET6 +# define AF_INET6 10 +#endif + +static char *progname; +static int verbose = 1; +static int errors; +static int notfound; + +/* verbosity level: + * <0 - bare result + * 0 - bare result and error messages + * 1 - readable result + * 2 - received packet contents and `trying ...' stuff + * 3 - sent and received packet contents + */ + +static void die(int errnum, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + if (errnum) fprintf(stderr, ": %s\n", strerror(errnum)); + else putc('\n', stderr); + fflush(stderr); + exit(1); +} + +static const char *dns_xntop(int af, const void *src) { + static char buf[6*5+4*4]; + return dns_ntop(af, src, buf, sizeof(buf)); +} + +struct query { + const char *name; /* original query string */ + unsigned char *dn; /* the DN being looked up */ + enum dns_type qtyp; /* type of the query */ +}; + +static void query_free(struct query *q) { + free(q->dn); + free(q); +} + +static struct query * +query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) { + struct query *q = malloc(sizeof(*q)); + unsigned l = dns_dnlen(dn); + unsigned char *cdn = malloc(l); + if (!q || !cdn) die(0, "out of memory"); + memcpy(cdn, dn, l); + q->name = name; + q->dn = cdn; + q->qtyp = qtyp; + return q; +} + +static enum dns_class qcls = DNS_C_IN; + +static void +dnserror(struct query *q, int errnum) { + if (verbose >= 0) + fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname, + dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum)); + if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA) + ++notfound; + else + ++errors; + query_free(q); +} + +static const unsigned char * +printtxt(const unsigned char *c) { + unsigned n = *c++; + const unsigned char *e = c + n; + if (verbose > 0) while(c < e) { + if (*c < ' ' || *c >= 127) printf("\\%03u", *c); + else if (*c == '\\' || *c == '"') printf("\\%c", *c); + else putchar(*c); + ++c; + } + else + fwrite(c, n, 1, stdout); + return e; +} + +static void +printhex(const unsigned char *c, const unsigned char *e) { + while(c < e) + printf("%02x", *c++); +} + +static unsigned char to_b64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static void +printb64(const unsigned char *c, const unsigned char *e) { + while(c < e) { + putchar(to_b64[c[0] >> 2]); + if (c+1 < e) { + putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]); + if (c+2 < e) { + putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]); + putchar(to_b64[c[2] & 0x3f]); + } + else { + putchar(to_b64[(c[1] & 0xf) << 2]); + putchar('='); + break; + } + } + else { + putchar(to_b64[(c[0] & 0x3) << 4]); + putchar('='); + putchar('='); + break; + } + c += 3; + } +} + +static void +printdate(time_t time) { + struct tm *tm = gmtime(&time); + printf("%04d%02d%02d%02d%02d%02d", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static void +printrr(const struct dns_parse *p, struct dns_rr *rr) { + const unsigned char *pkt = p->dnsp_pkt; + const unsigned char *end = p->dnsp_end; + const unsigned char *dptr = rr->dnsrr_dptr; + const unsigned char *dend = rr->dnsrr_dend; + unsigned char *dn = rr->dnsrr_dn; + const unsigned char *c; + unsigned n; + + if (verbose > 0) { + if (verbose > 1) { + if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) { + printf(";EDNS%d OPT record (UDPsize: %d, ERcode: %d, Flags: 0x%02x): %d bytes\n", + (rr->dnsrr_ttl>>16) & 0xff, /* version */ + rr->dnsrr_cls, /* udp size */ + (rr->dnsrr_ttl>>24) & 0xff, /* extended rcode */ + rr->dnsrr_ttl & 0xffff, /* flags */ + rr->dnsrr_dsz); + return; + } + n = printf("%s.", dns_dntosp(rr->dnsrr_dn)); + printf("%s%u\t%s\t%s\t", + n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t", + rr->dnsrr_ttl, + dns_classname(rr->dnsrr_cls), + dns_typename(rr->dnsrr_typ)); + } + else + printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ)); + } + + switch(rr->dnsrr_typ) { + + case DNS_T_CNAME: + case DNS_T_PTR: + case DNS_T_NS: + case DNS_T_MB: + case DNS_T_MD: + case DNS_T_MF: + case DNS_T_MG: + case DNS_T_MR: + if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%s.", dns_dntosp(dn)); + break; + + case DNS_T_A: + if (rr->dnsrr_dsz != 4) goto xperr; + printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]); + break; + + case DNS_T_AAAA: + if (rr->dnsrr_dsz != 16) goto xperr; + printf("%s", dns_xntop(AF_INET6, dptr)); + break; + + case DNS_T_MX: + c = dptr + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + printf("%d %s.", dns_get16(dptr), dns_dntosp(dn)); + break; + + case DNS_T_TXT: + /* first verify it */ + for(c = dptr; c < dend; c += n) { + n = *c++; + if (c + n > dend) goto xperr; + } + c = dptr; n = 0; + while (c < dend) { + if (verbose > 0) printf(n++ ? "\" \"":"\""); + c = printtxt(c); + } + if (verbose > 0) putchar('"'); + break; + + case DNS_T_HINFO: /* CPU, OS */ + c = dptr; + n = *c++; if ((c += n) >= dend) goto xperr; + n = *c++; if ((c += n) != dend) goto xperr; + c = dptr; + if (verbose > 0) putchar('"'); + c = printtxt(c); + if (verbose > 0) printf("\" \""); else putchar(' '); + printtxt(c); + if (verbose > 0) putchar('"'); + break; + + case DNS_T_WKS: + c = dptr; + if (dptr + 4 + 2 >= end) goto xperr; + printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]); + c = dptr + 5; + for (n = 0; c < dend; ++c, n += 8) { + if (*c) { + unsigned b; + for (b = 0; b < 8; ++b) + if (*c & (1 << (7-b))) printf(" %d", n + b); + } + } + break; + + case DNS_T_SRV: /* prio weight port targetDN */ + c = dptr; + c += 2 + 2 + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + c = dptr; + printf("%d %d %d %s.", + dns_get16(c+0), dns_get16(c+2), dns_get16(c+4), + dns_dntosp(dn)); + break; + + case DNS_T_NAPTR: /* order pref flags serv regexp repl */ + c = dptr; + c += 4; /* order, pref */ + for (n = 0; n < 3; ++n) + if (c >= dend) goto xperr; + else c += *c + 1; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr; + c = dptr; + printf("%u %u", dns_get16(c+0), dns_get16(c+2)); + c += 4; + for(n = 0; n < 3; ++n) { + putchar(' '); + if (verbose > 0) putchar('"'); + c = printtxt(c); + if (verbose > 0) putchar('"'); + } + printf(" %s.", dns_dntosp(dn)); + break; + + case DNS_T_KEY: + case DNS_T_DNSKEY: + /* flags(2) proto(1) algo(1) pubkey */ + case DNS_T_DS: + case DNS_T_DLV: + /* ktag(2) proto(1) algo(1) pubkey */ + c = dptr; + if (c + 2 + 1 + 1 > dend) goto xperr; + printf("%d %d %d", dns_get16(c), c[2], c[3]); + c += 2 + 1 + 1; + if (c < dend) { + putchar(' '); + printb64(c, dend); + } + break; + + case DNS_T_SIG: + case DNS_T_RRSIG: + /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */ + c = dptr; + c += 2 + 1 + 1 + 4 + 4 + 4 + 2; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%s %u %u %u ", + dns_typename(dns_get16(dptr)), dptr[2], dptr[3], dns_get32(dptr+4)); + printdate(dns_get32(dptr+8)); + putchar(' '); + printdate(dns_get32(dptr+12)); + printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn)); + printb64(c, dend); + break; + + case DNS_T_SSHFP: /* algo(1), fp type(1), fp... */ + if (dend < dptr + 3) goto xperr; + printf("%u %u ", dptr[0], dptr[1]); /* algo, fp type */ + printhex(dptr + 2, dend); + break; + +#if 0 /* unused RR types? */ + case DNS_T_NSEC: /* nextDN bitmaps */ + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr; + printf("%s.", dns_dntosp(dn)); + unfinished. + break; +#endif + + + case DNS_T_SOA: + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + c + 4*5 != dend) + goto xperr; + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + printf("%u %u %u %u %u", + dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8), + dns_get32(dptr+12), dns_get32(dptr+16)); + break; + + case DNS_T_MINFO: + c = dptr; + if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || + c != dend) + goto xperr; + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s. ", dns_dntosp(dn)); + dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN); + printf("%s.", dns_dntosp(dn)); + break; + + case DNS_T_NULL: + default: + printhex(dptr, dend); + break; + } + putchar('\n'); + return; + +xperr: + printf("\n"); + ++errors; +} + +static int +printsection(struct dns_parse *p, int nrr, const char *sname) { + struct dns_rr rr; + int r; + if (!nrr) return 0; + if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr); + + p->dnsp_rrl = nrr; + while((r = dns_nextrr(p, &rr)) > 0) + printrr(p, &rr); + if (r < 0) printf("<>\n"); + return r; +} + +/* dbgcb will only be called if verbose > 1 */ +static void +dbgcb(int code, const struct sockaddr *sa, unsigned slen, + const unsigned char *pkt, int r, + const struct dns_query *unused_q, void *unused_data) { + struct dns_parse p; + const unsigned char *cur, *end; + int numqd; + + if (code > 0) { + printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt))); + printf(";; sending %d bytes query to ", r); + } + else + printf(";; received %d bytes response from ", r); + if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in)) + printf("%s port %d\n", + dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr), + htons(((struct sockaddr_in*)sa)->sin_port)); +#ifdef HAVE_IPv6 + else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6)) + printf("%s port %d\n", + dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr), + htons(((struct sockaddr_in6*)sa)->sin6_port)); +#endif + else + printf("<>\n", sa->sa_family); + if (code > 0 && verbose < 3) { + putchar('\n'); + return; + } + + if (code == -2) printf(";; reply from unexpected source\n"); + if (code == -5) printf(";; reply to a query we didn't sent (or old)\n"); + if (r < DNS_HSIZE) { + printf(";; short packet (%d bytes)\n", r); + return; + } + if (dns_opcode(pkt) != 0) + printf(";; unexpected opcode %d\n", dns_opcode(pkt)); + if (dns_tc(pkt) != 0) + printf(";; warning: TC bit set, probably incomplete reply\n"); + + printf(";; ->>HEADER<<- opcode: "); + switch(dns_opcode(pkt)) { + case 0: printf("QUERY"); break; + case 1: printf("IQUERY"); break; + case 2: printf("STATUS"); break; + default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break; + } + printf(", status: %s, id: %d, size: %d\n;; flags:", + dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r); + if (dns_qr(pkt)) printf(" qr"); + if (dns_aa(pkt)) printf(" aa"); + if (dns_tc(pkt)) printf(" tc"); + if (dns_rd(pkt)) printf(" rd"); + if (dns_ra(pkt)) printf(" ra"); + /* if (dns_z(pkt)) printf(" z"); only one reserved bit left */ + if (dns_ad(pkt)) printf(" ad"); + if (dns_cd(pkt)) printf(" cd"); + numqd = dns_numqd(pkt); + printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n", + numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt)); + if (numqd != 1) + printf(";; unexpected number of entries in QUERY section: %d\n", + numqd); + printf("\n;; QUERY SECTION (%d):\n", numqd); + cur = dns_payload(pkt); + end = pkt + r; + while(numqd--) { + if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 || + cur + 4 > end) { + printf("; invalid query section\n"); + return; + } + r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf)); + printf("%s%s\t%s\n", + r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t", + dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur))); + cur += 4; + } + + p.dnsp_pkt = pkt; + p.dnsp_cur = p.dnsp_ans = cur; + p.dnsp_end = end; + p.dnsp_qdn = NULL; + p.dnsp_qcls = p.dnsp_qtyp = 0; + p.dnsp_ttl = 0xffffffffu; + p.dnsp_nrr = 0; + + r = printsection(&p, dns_numan(pkt), "ANSWER"); + if (r == 0) + r = printsection(&p, dns_numns(pkt), "AUTHORITY"); + if (r == 0) + r = printsection(&p, dns_numar(pkt), "ADDITIONAL"); + putchar('\n'); +} + +static void dnscb(struct dns_ctx *ctx, void *result, void *data) { + int r = dns_status(ctx); + struct query *q = data; + struct dns_parse p; + struct dns_rr rr; + unsigned nrr; + unsigned char dn[DNS_MAXDN]; + const unsigned char *pkt, *cur, *end; + if (!result) { + dnserror(q, r); + return; + } + pkt = result; end = pkt + r; cur = dns_payload(pkt); + dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + dns_initparse(&p, NULL, pkt, cur, end); + p.dnsp_qcls = p.dnsp_qtyp = 0; + nrr = 0; + while((r = dns_nextrr(&p, &rr)) > 0) { + if (!dns_dnequal(dn, rr.dnsrr_dn)) continue; + if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) && + (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ)) + ++nrr; + else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) { + if (dns_getdn(pkt, &rr.dnsrr_dptr, end, + p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 || + rr.dnsrr_dptr != rr.dnsrr_dend) { + r = DNS_E_PROTOCOL; + break; + } + else { + if (verbose == 1) { + printf("%s.", dns_dntosp(dn)); + printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf)); + } + dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn)); + } + } + } + if (!r && !nrr) + r = DNS_E_NODATA; + if (r < 0) { + dnserror(q, r); + free(result); + return; + } + if (verbose < 2) { /* else it is already printed by dbgfn */ + dns_rewind(&p, NULL); + p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp; + p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls; + while(dns_nextrr(&p, &rr)) + printrr(&p, &rr); + } + free(result); + query_free(q); +} + +int main(int argc, char **argv) { + int i; + int fd; + fd_set fds; + struct timeval tv; + time_t now; + char *ns[DNS_MAXSERV]; + int nns = 0; + struct query *q; + enum dns_type qtyp = 0; + struct dns_ctx *nctx = NULL; + int flags = 0; + + if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; + else argv[0] = ++progname; + + if (argc <= 1) + die(0, "try `%s -h' for help", progname); + + if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL))) + die(errno, "unable to initialize dns library"); + /* we keep two dns contexts: one may be needed to resolve + * nameservers if given as names, using default options. + */ + + while((i = getopt(argc, argv, "vqt:c:an:o:f:h")) != EOF) switch(i) { + case 'v': ++verbose; break; + case 'q': --verbose; break; + case 't': + if (optarg[0] == '*' && !optarg[1]) + i = DNS_T_ANY; + else if ((i = dns_findtypename(optarg)) <= 0) + die(0, "unrecognized query type `%s'", optarg); + qtyp = i; + break; + case 'c': + if (optarg[0] == '*' && !optarg[1]) + i = DNS_C_ANY; + else if ((i = dns_findclassname(optarg)) < 0) + die(0, "unrecognized query class `%s'", optarg); + qcls = i; + break; + case 'a': + qtyp = DNS_T_ANY; + ++verbose; + break; + case 'n': + if (nns >= DNS_MAXSERV) + die(0, "too many nameservers, %d max", DNS_MAXSERV); + ns[nns++] = optarg; + break; + case 'o': + case 'f': { + char *opt; + const char *const delim = " \t,;"; + for(opt = strtok(optarg, delim); opt != NULL; opt = strtok(NULL, delim)) { + if (dns_set_opts(NULL, optarg) == 0) + ; + else if (strcmp(opt, "aa") == 0) flags |= DNS_AAONLY; + else if (strcmp(optarg, "nord") == 0) flags |= DNS_NORD; + else if (strcmp(optarg, "dnssec") == 0) flags |= DNS_SET_DO; + else if (strcmp(optarg, "do") == 0) flags |= DNS_SET_DO; + else if (strcmp(optarg, "cd") == 0) flags |= DNS_SET_CD; + else + die(0, "invalid option: `%s'", opt); + } + break; + } + case 'h': + printf( +"%s: simple DNS query tool (using udns version %s)\n" +"Usage: %s [options] domain-name...\n" +"where options are:\n" +" -h - print this help and exit\n" +" -v - be more verbose\n" +" -q - be less verbose\n" +" -t type - set query type (A, AAA, PTR etc)\n" +" -c class - set query class (IN (default), CH, HS, *)\n" +" -a - equivalent to -t ANY -v\n" +" -n ns - use given nameserver(s) instead of default\n" +" (may be specified multiple times)\n" +" -o opt,opt,... (comma- or space-separated list,\n" +" may be specified more than once):\n" +" set resovler options (the same as setting $RES_OPTIONS):\n" +" timeout:sec - initial query timeout\n" +" attempts:num - number of attempt to resovle a query\n" +" ndots:num - if name has more than num dots, lookup it before search\n" +" port:num - port number for queries instead of default 53\n" +" udpbuf:num - size of UDP buffer (use EDNS0 if >512)\n" +" or query flags:\n" +" aa,nord,dnssec,do,cd - set query flag (auth-only, no recursion,\n" +" enable DNSSEC (DNSSEC Ok), check disabled)\n" + , progname, dns_version(), progname); + return 0; + default: + die(0, "try `%s -h' for help", progname); + } + + argc -= optind; argv += optind; + if (!argc) + die(0, "no name(s) to query specified"); + + if (nns) { + /* if nameservers given as names, resolve them. + * We only allow IPv4 nameservers as names for now. + * Ok, it is easy enouth to try both AAAA and A, + * but the question is what to do by default. + */ + struct sockaddr_in sin; + int j, r = 0, opened = 0; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1)); + dns_add_serv(NULL, NULL); + for(i = 0; i < nns; ++i) { + if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) { + struct dns_rr_a4 *rr; + if (!opened) { + if (dns_open(nctx) < 0) + die(errno, "unable to initialize dns context"); + opened = 1; + } + rr = dns_resolve_a4(nctx, ns[i], 0); + if (!rr) + die(0, "unable to resolve nameserver %s: %s", + ns[i], dns_strerror(dns_status(nctx))); + for(j = 0; j < rr->dnsa4_nrr; ++j) { + sin.sin_addr = rr->dnsa4_addr[j]; + if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0) + break; + } + free(rr); + } + else + r = dns_add_serv_s(NULL, (struct sockaddr *)&sin); + if (r < 0) + die(errno, "unable to add nameserver %s", + dns_xntop(AF_INET, &sin.sin_addr)); + } + } + dns_free(nctx); + + fd = dns_open(NULL); + if (fd < 0) + die(errno, "unable to initialize dns context"); + + if (verbose > 1) + dns_set_dbgfn(NULL, dbgcb); + + if (flags) + dns_set_opt(NULL, DNS_OPT_FLAGS, flags); + + for (i = 0; i < argc; ++i) { + char *name = argv[i]; + union { + struct in_addr addr; + struct in6_addr addr6; + } a; + unsigned char dn[DNS_MAXDN]; + enum dns_type l_qtyp = 0; + int abs; + if (dns_pton(AF_INET, name, &a.addr) > 0) { + dns_a4todn(&a.addr, 0, dn, sizeof(dn)); + l_qtyp = DNS_T_PTR; + abs = 1; + } +#ifdef HAVE_IPv6 + else if (dns_pton(AF_INET6, name, &a.addr6) > 0) { + dns_a6todn(&a.addr6, 0, dn, sizeof(dn)); + l_qtyp = DNS_T_PTR; + abs = 1; + } +#endif + else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs)) + die(0, "invalid name `%s'\n", name); + else + l_qtyp = DNS_T_A; + if (qtyp) l_qtyp = qtyp; + q = query_new(name, dn, l_qtyp); + if (abs) abs = DNS_NOSRCH; + if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q)) + dnserror(q, dns_status(NULL)); + } + + FD_ZERO(&fds); + now = 0; + while((i = dns_timeouts(NULL, -1, now)) > 0) { + FD_SET(fd, &fds); + tv.tv_sec = i; + tv.tv_usec = 0; + i = select(fd+1, &fds, 0, 0, &tv); + now = time(NULL); + if (i > 0) dns_ioevent(NULL, now); + } + + return errors ? 1 : notfound ? 100 : 0; +} diff --git a/ami_test_client/libc-jss/udns/ex-rdns.c b/ami_test_client/libc-jss/udns/ex-rdns.c new file mode 100644 index 0000000000..1e1e90d4b1 --- /dev/null +++ b/ami_test_client/libc-jss/udns/ex-rdns.c @@ -0,0 +1,114 @@ +/* ex-rdns.c + parallel rDNS resolver example - read IP addresses from stdin, + write domain names to stdout + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "udns.h" + +static int curq; + +static const char *n2ip(const unsigned char *c) { + static char b[sizeof("255.255.255.255")]; + sprintf(b, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]); + return b; +} +static void dnscb(struct dns_ctx *ctx, struct dns_rr_ptr *rr, void *data) { + const char *ip = n2ip((unsigned char *)&data); + int i; + --curq; + if (rr) { + printf("%s", ip); + for(i = 0; i < rr->dnsptr_nrr; ++i) + printf(" %s", rr->dnsptr_ptr[i]); + putchar('\n'); + free(rr); + } + else + fprintf(stderr, "%s: %s\n", ip, dns_strerror(dns_status(ctx))); +} + +int main(int argc, char **argv) { + int c; + time_t now; + int maxq = 10; + struct pollfd pfd; + char linebuf[1024]; + char *eol; + int eof; + + if (dns_init(NULL, 1) < 0) { + fprintf(stderr, "unable to initialize dns library\n"); + return 1; + } + while((c = getopt(argc, argv, "m:r")) != EOF) switch(c) { + case 'm': maxq = atoi(optarg); break; + case 'r': + dns_set_opt(0, DNS_OPT_FLAGS, + dns_set_opt(0, DNS_OPT_FLAGS, -1) | DNS_NORD); + break; + default: return 1; + } + if (argc != optind) return 1; + + pfd.fd = dns_sock(0); + pfd.events = POLLIN; + now = time(NULL); + c = optind; + eof = 0; + while(curq || !eof) { + if (!eof && curq < maxq) { + union { struct in_addr a; void *p; } pa; + if (!fgets(linebuf, sizeof(linebuf), stdin)) { + eof = 1; + continue; + } + eol = strchr(linebuf, '\n'); + if (eol) *eol = '\0'; + if (!linebuf[0]) continue; + if (dns_pton(AF_INET, linebuf, &pa.a) <= 0) + fprintf(stderr, "%s: invalid address\n", linebuf); + else if (dns_submit_a4ptr(0, &pa.a, dnscb, pa.p) == 0) + fprintf(stderr, "%s: unable to submit query: %s\n", + linebuf, dns_strerror(dns_status(0))); + else + ++curq; + continue; + } + if (curq) { + c = dns_timeouts(0, -1, now); + c = poll(&pfd, 1, c < 0 ? -1 : c * 1000); + now = time(NULL); + if (c) + dns_ioevent(0, now); + } + } + return 0; +} diff --git a/ami_test_client/libc-jss/udns/getopt.c b/ami_test_client/libc-jss/udns/getopt.c new file mode 100644 index 0000000000..e15a7a0579 --- /dev/null +++ b/ami_test_client/libc-jss/udns/getopt.c @@ -0,0 +1,165 @@ +/* getopt.c + * Simple getopt() implementation. + * + * Standard interface: + * extern int getopt(int argc, char *const *argv, const char *opts); + * extern int optind; current index in argv[] + * extern char *optarg; argument for the current option + * extern int optopt; the current option + * extern int opterr; to control error printing + * + * Some minor extensions: + * ignores leading `+' sign in opts[] (unemplemented GNU extension) + * handles optional arguments, in form "x::" in opts[] + * if opts[] starts with `:', will return `:' in case of missing required + * argument, instead of '?'. + * + * Compile with -DGETOPT_NO_OPTERR to never print errors internally. + * Compile with -DGETOPT_NO_STDIO to use write() calls instead of fprintf() for + * error reporting (ignored with -DGETOPT_NO_OPTERR). + * Compile with -DGETOPT_CLASS=static to get static linkage. + * Compile with -DGETOPT_MY to redefine all visible symbols to be prefixed + * with "my_", like my_getopt instead of getopt. + * Compile with -DTEST to get a test executable. + * + * Written by Michael Tokarev. Public domain. + */ + +#include + +#ifndef GETOPT_CLASS +# define GETOPT_CLASS +#endif +#ifdef GETOPT_MY +# define optarg my_optarg +# define optind my_optind +# define opterr my_opterr +# define optopt my_optopt +# define getopt my_getopt +#endif + +GETOPT_CLASS char *optarg /* = NULL */; +GETOPT_CLASS int optind = 1; +GETOPT_CLASS int opterr = 1; +GETOPT_CLASS int optopt; + +static char *nextc /* = NULL */; + +#if defined(GETOPT_NO_OPTERR) + +#define printerr(argv, msg) + +#elif defined(GETOPT_NO_STDIO) + +extern int write(int, void *, int); + +static void printerr(char *const *argv, const char *msg) { + if (opterr) { + char buf[64]; + unsigned pl = strlen(argv[0]); + unsigned ml = strlen(msg); + char *p; + if (pl + /*": "*/2 + ml + /*" -- c\n"*/6 > sizeof(buf)) { + write(2, argv[0], pl); + p = buf; + } + else { + memcpy(buf, argv[0], ml); + p = buf + pl; + } + *p++ = ':'; *p++ = ' '; + memcpy(p, msg, ml); p += ml; + *p++ = ' '; *p++ = '-'; *p++ = '-'; *p++ = ' '; + *p++ = optopt; + *p++ = '\n'; + write(2, buf, p - buf); + } +} + +#else + +#include +static void printerr(char *const *argv, const char *msg) { + if (opterr) + fprintf(stderr, "%s: %s -- %c\n", argv[0], msg, optopt); +} + +#endif + +GETOPT_CLASS int getopt(int argc, char *const *argv, const char *opts) { + char *p; + + optarg = 0; + if (*opts == '+') /* GNU extension (permutation) - isn't supported */ + ++opts; + + if (!optind) { /* a way to reset things */ + nextc = 0; + optind = 1; + } + + if (!nextc || !*nextc) { /* advance to the next argv element */ + /* done scanning? */ + if (optind >= argc) + return -1; + /* not an optional argument */ + if (argv[optind][0] != '-') + return -1; + /* bare `-' */ + if (argv[optind][1] == '\0') + return -1; + /* special case `--' argument */ + if (argv[optind][1] == '-' && argv[optind][2] == '\0') { + ++optind; + return -1; + } + nextc = argv[optind] + 1; + } + + optopt = *nextc++; + if (!*nextc) + ++optind; + p = strchr(opts, optopt); + if (!p || optopt == ':') { + printerr(argv, "illegal option"); + return '?'; + } + if (p[1] == ':') { + if (*nextc) { + optarg = nextc; + nextc = NULL; + ++optind; + } + else if (p[2] != ':') { /* required argument */ + if (optind >= argc) { + printerr(argv, "option requires an argument"); + return *opts == ':' ? ':' : '?'; + } + else + optarg = argv[optind++]; + } + } + return optopt; +} + +#ifdef TEST + +#include + +int main(int argc, char **argv) { + int c; + while((c = getopt(argc, argv, "ab:c::")) != -1) switch(c) { + case 'a': + case 'b': + case 'c': + printf("option %c %s\n", c, optarg ? optarg : "(none)"); + break; + default: + return -1; + } + for(c = optind; c < argc; ++c) + printf("non-opt: %s\n", argv[c]); + return 0; +} + +#endif diff --git a/ami_test_client/libc-jss/udns/inet_XtoX.c b/ami_test_client/libc-jss/udns/inet_XtoX.c new file mode 100644 index 0000000000..50b5f8e81f --- /dev/null +++ b/ami_test_client/libc-jss/udns/inet_XtoX.c @@ -0,0 +1,327 @@ +/* inet_XtoX.c + * Simple implementation of the following functions: + * inet_ntop(), inet_ntoa(), inet_pton(), inet_aton(). + * + * Differences from traditional implementaitons: + * o modifies destination buffers even on error return. + * o no fancy (hex, or 1.2) input support in inet_aton() + * o inet_aton() does not accept junk after an IP address. + * o inet_ntop(AF_INET) requires at least 16 bytes in dest, + * and inet_ntop(AF_INET6) at least 40 bytes + * (traditional inet_ntop() will try to fit anyway) + * + * Compile with -Dinet_XtoX_prefix=pfx_ to have pfx_*() instead of inet_*() + * Compile with -Dinet_XtoX_no_ntop or -Dinet_XtoX_no_pton + * to disable net2str or str2net conversions. + * + * #define inet_XtoX_prototypes and #include "this_file.c" + * to get function prototypes only (but not for inet_ntoa()). + * #define inet_XtoX_decl to be `static' for static visibility, + * or use __declspec(dllexport) or somesuch... + * + * Compile with -DTEST to test against stock implementation. + * + * Written by Michael Tokarev. Public domain. + */ + +#ifdef inet_XtoX_prototypes + +struct in_addr; + +#else + +#include + +#ifdef TEST + +# include +# include +# include +# include +# include +# include +# include +# undef inet_XtoX_prefix +# define inet_XtoX_prefix mjt_inet_ +# undef inet_XtoX_no_ntop +# undef inet_XtoX_no_pton + +#else /* !TEST */ + +struct in_addr { /* declare it here to avoid messing with headers */ + unsigned char x[4]; +}; + +#endif /* TEST */ + +#endif /* inet_XtoX_prototypes */ + +#ifndef inet_XtoX_prefix +# define inet_XtoX_prefix inet_ +#endif +#ifndef inet_XtoX_decl +# define inet_XtoX_decl /*empty*/ +#endif + +#define cc2_(x,y) cc2__(x,y) +#define cc2__(x,y) x##y +#define fn(x) cc2_(inet_XtoX_prefix,x) + +#ifndef inet_XtoX_no_ntop + +inet_XtoX_decl const char * +fn(ntop)(int af, const void *src, char *dst, unsigned size); + +#ifndef inet_XtoX_prototypes + +static int mjt_ntop4(const void *_src, char *dst, int size) { + unsigned i, x, r; + char *p; + const unsigned char *s = _src; + if (size < 4*4) /* for simplicity, disallow non-max-size buffer */ + return 0; + for (i = 0, p = dst; i < 4; ++i) { + if (i) *p++ = '.'; + x = r = s[i]; + if (x > 99) { *p++ = (char)(r / 100 + '0'); r %= 100; } + if (x > 9) { *p++ = (char)(r / 10 + '0'); r %= 10; } + *p++ = (char)(r + '0'); + } + *p = '\0'; + return 1; +} + +static char *hexc(char *p, unsigned x) { + static char hex[16] = "0123456789abcdef"; + if (x > 0x0fff) *p++ = hex[(x >>12) & 15]; + if (x > 0x00ff) *p++ = hex[(x >> 8) & 15]; + if (x > 0x000f) *p++ = hex[(x >> 4) & 15]; + *p++ = hex[x & 15]; + return p; +} + +static int mjt_ntop6(const void *_src, char *dst, int size) { + unsigned i; + unsigned short w[8]; + unsigned bs = 0, cs = 0; + unsigned bl = 0, cl = 0; + char *p; + const unsigned char *s = _src; + + if (size < 40) /* for simplicity, disallow non-max-size buffer */ + return 0; + + for(i = 0; i < 8; ++i, s += 2) { + w[i] = (((unsigned short)(s[0])) << 8) | s[1]; + if (!w[i]) { + if (!cl++) cs = i; + } + else { + if (cl > bl) bl = cl, bs = cs; + } + } + if (cl > bl) bl = cl, bs = cs; + p = dst; + if (bl == 1) + bl = 0; + if (bl) { + for(i = 0; i < bs; ++i) { + if (i) *p++ = ':'; + p = hexc(p, w[i]); + } + *p++ = ':'; + i += bl; + if (i == 8) + *p++ = ':'; + } + else + i = 0; + for(; i < 8; ++i) { + if (i) *p++ = ':'; + if (i == 6 && !bs && (bl == 6 || (bl == 5 && w[5] == 0xffff))) + return mjt_ntop4(s - 4, p, size - (p - dst)); + p = hexc(p, w[i]); + } + *p = '\0'; + return 1; +} + +inet_XtoX_decl const char * +fn(ntop)(int af, const void *src, char *dst, unsigned size) { + switch(af) { + /* don't use AF_*: don't mess with headers */ + case 2: /* AF_INET */ if (mjt_ntop4(src, dst, size)) return dst; break; + case 10: /* AF_INET6 */ if (mjt_ntop6(src, dst, size)) return dst; break; + default: errno = EAFNOSUPPORT; return (char*)0; + } + errno = ENOSPC; + return (char*)0; +} + +inet_XtoX_decl const char * +fn(ntoa)(struct in_addr addr) { + static char buf[4*4]; + mjt_ntop4(&addr, buf, sizeof(buf)); + return buf; +} + +#endif /* inet_XtoX_prototypes */ +#endif /* inet_XtoX_no_ntop */ + +#ifndef inet_XtoX_no_pton + +inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst); +inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr); + +#ifndef inet_XtoX_prototypes + +static int mjt_pton4(const char *c, void *dst) { + unsigned char *a = dst; + unsigned n, o; + for (n = 0; n < 4; ++n) { + if (*c < '0' || *c > '9') + return 0; + o = *c++ - '0'; + while(*c >= '0' && *c <= '9') + if ((o = o * 10 + (*c++ - '0')) > 255) + return 0; + if (*c++ != (n == 3 ? '\0' : '.')) + return 0; + *a++ = (unsigned char)o; + } + return 1; +} + +static int mjt_pton6(const char *c, void *dst) { + unsigned short w[8], *a = w, *z, *i; + unsigned v, o; + const char *sc; + unsigned char *d = dst; + if (*c != ':') z = (unsigned short*)0; + else if (*++c != ':') return 0; + else ++c, z = a; + i = 0; + for(;;) { + v = 0; + sc = c; + for(;;) { + if (*c >= '0' && *c <= '9') o = *c - '0'; + else if (*c >= 'a' && *c <= 'f') o = *c - 'a' + 10; + else if (*c >= 'A' && *c <= 'F') o = *c - 'A' + 10; + else break; + v = (v << 4) | o; + if (v > 0xffff) return 0; + ++c; + } + if (sc == c) { + if (z == a && !*c) + break; + else + return 0; + } + if (*c == ':') { + if (a >= w + 8) + return 0; + *a++ = v; + if (*++c == ':') { + if (z) + return 0; + z = a; + if (!*++c) + break; + } + } + else if (!*c) { + if (a >= w + 8) + return 0; + *a++ = v; + break; + } + else if (*c == '.') { + if (a > w + 6) + return 0; + if (!mjt_pton4(sc, d)) + return 0; + *a++ = ((unsigned)(d[0]) << 8) | d[1]; + *a++ = ((unsigned)(d[2]) << 8) | d[3]; + break; + } + else + return 0; + } + v = w + 8 - a; + if ((v && !z) || (!v && z)) + return 0; + for(i = w; ; ++i) { + if (i == z) + while(v--) { *d++ = '\0'; *d++ = '\0'; } + if (i >= a) + break; + *d++ = (unsigned char)((*i >> 8) & 255); + *d++ = (unsigned char)(*i & 255); + } + return 1; +} + +inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst) { + switch(af) { + /* don't use AF_*: don't mess with headers */ + case 2 /* AF_INET */: return mjt_pton4(src, dst); + case 10 /* AF_INET6 */: return mjt_pton6(src, dst); + default: errno = EAFNOSUPPORT; return -1; + } +} + +inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr) { + return mjt_pton4(src, addr); +} + +#endif /* inet_XtoX_prototypes */ + +#endif /* inet_XtoX_no_pton */ + +#ifdef TEST + +int main(int argc, char **argv) { + int i; + char n0[16], n1[16]; + char p0[64], p1[64]; + int af = AF_INET; + int pl = sizeof(p0); + int r0, r1; + const char *s0, *s1; + + while((i = getopt(argc, argv, "46a:p:")) != EOF) switch(i) { + case '4': af = AF_INET; break; + case '6': af = AF_INET6; break; + case 'a': case 'p': pl = atoi(optarg); break; + default: return 1; + } + for(i = optind; i < argc; ++i) { + char *a = argv[i]; + + printf("%s:\n", a); + r0 = inet_pton(af, a, n0); + printf(" p2n stock: %s\n", + (r0 < 0 ? "(notsupp)" : !r0 ? "(inval)" : fn(ntop)(af,n0,p0,sizeof(p0)))); + r1 = fn(pton)(af, a, n1); + printf(" p2n this : %s\n", + (r1 < 0 ? "(notsupp)" : !r1 ? "(inval)" : fn(ntop)(af,n1,p1,sizeof(p1)))); + + if ((r0 > 0) != (r1 > 0) || + (r0 > 0 && r1 > 0 && memcmp(n0, n1, af == AF_INET ? 4 : 16) != 0)) + printf(" DIFFER!\n"); + + s0 = inet_ntop(af, n1, p0, pl); + printf(" n2p stock: %s\n", s0 ? s0 : "(inval)"); + s1 = fn(ntop)(af, n1, p1, pl); + printf(" n2p this : %s\n", s1 ? s1 : "(inval)"); + if ((s0 != 0) != (s1 != 0) || + (s0 && s1 && strcmp(s0, s1) != 0)) + printf(" DIFFER!\n"); + + } + return 0; +} + +#endif /* TEST */ diff --git a/ami_test_client/libc-jss/udns/rblcheck.1 b/ami_test_client/libc-jss/udns/rblcheck.1 new file mode 100644 index 0000000000..6c822c01c0 --- /dev/null +++ b/ami_test_client/libc-jss/udns/rblcheck.1 @@ -0,0 +1,151 @@ +.\" rblcheck.1 +.\" rblckeck manpage +.\" +.\" Copyright (C) 2005 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH rblckeck 1 "Apr 2005" "User Utilities" + +.SH NAME +rblckeck \- DNSBL lookup utility + +.SH SYNOPSYS +.B rblcheck +.RB [\| \-s +.IR zone \|] +.RB [\| \-S +.IR zone\-file \|] +.RB [\| \-c \|] +.RB [\| \-tmvq \|] +.RB [\| \-n +.IR nsaddr \|] +.IR address \|.\|.\|. + +.SH DESCRIPTION +.B rblcheck +is a simple command-line to perform DNSBL (DNS-based blocklists) lookups. +For every IP address (or a name, in which case it will be resolved to an +address first), the utility verifies whenever it is listed in a (list of) +DNS blocklists specified with +.B \-s +or +.B \-S +options, optionally obtains text assotiated with the listing (usually it +is either some description about the reason of the listing or an URL +referring to such a description), and displays results on standard output. +.PP +The program is implemented on top of +.BR udns (3) +library. + +.SH OPTIONS + +The following options are recognized by +.BR rblcheck : + +.TP +.B \-s \fIzone\fR +add the given \fIzone\fR DNSBL name to the list of active zones. +.TP +.B \-S \fIzone-file\fR +add list of zones from the named \fIzone-file\fR to the list of +active zones (the file specifies one zone as the first word on a +line, empty lines and lines starting with `#' character are ignored). +.TP +.B \-c +reset active zone list. +.TP +.B \-v +be more verbose, produce more detailed output. +.TP +.B \-q +the opposite for \fB\-v\fR -- produce less detailed output. +.TP +.B \-t +obtain text for listed addresses. +.TP +.B \-n \fInsaddr\fR +Use the given nameserver (given as IPv4 or IPv6 address) instead of the +default. The same effect may be achieved by setting $NSCACHEIP environment +variable. +.TP +.B \-m +stop after first hit, ie after the first address which is found to be +listed. + +.TP +.B \-h +print short help and exit. + +.PP +If no +.BR \-s , +.BR \-S +and +.B \-c +options are given, +.B rblcheck +will try to obtain list of zones using $RBLCHECK_ZONES environment variable, +or ~/.rblcheckrc, or /etc/rblckechrc files, in that order. If no zones are +found, it will exit unsuccessefully. + +.SH "RETURN VALUE" +When no addresses given are listed and no errors occured, +.B rblcheck +exits with code 0. If at least one address is listed, +.B rblcheck +returns 100. In case of DNS errors, +.B rblcheck +returns 2. + +.SH ENVIRONMENT + +.TP +.B $RBLCHECK_ZONES +if no +.BR \-s , +.B \-S +or +.B \-c +option is given, +.B rblcheck +tries this variable to obtain list of DNSBL zones to check against. + +.SH FILES + +.TP +$HOME/.rblcheckrc and /etc/rblcheckrc +if no +.BR \-s , +.B \-S +or +.B \-c +option is given, and no $RBLCHECK_ZONES environment variable is set, +.B rblcheck +will try the two files (the first one that exists) to obtain list of +DNSBL zones to check against. +Each line specifies one zone (only first word in each line is used). +Empty lines and lines starting with `#' character are ignored. + +.SH "SEE ALSO" +.BR dnsget (1) +.BR resolv.conf (5) +.BR udns (3). + +.SH AUTHOR +This program and manual pages are written by Michael Tokarev. diff --git a/ami_test_client/libc-jss/udns/rblcheck.c b/ami_test_client/libc-jss/udns/rblcheck.c new file mode 100644 index 0000000000..82d29deeeb --- /dev/null +++ b/ami_test_client/libc-jss/udns/rblcheck.c @@ -0,0 +1,378 @@ +/* rblcheck.c + dnsbl (rbl) checker application + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#ifdef WINDOWS +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#include "udns.h" + +#ifndef HAVE_GETOPT +# include "getopt.c" +#endif + +static const char *version = "udns-rblcheck 0.4"; +static char *progname; + +static void error(int die, const char *fmt, ...) { + va_list ap; + fprintf(stderr, "%s: ", progname); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); + putc('\n', stderr); + fflush(stderr); + if (die) + exit(1); +} + +struct rblookup { + struct ipcheck *parent; + struct in_addr key; + const char *zone; + struct dns_rr_a4 *addr; + struct dns_rr_txt *txt; +}; + +struct ipcheck { + const char *name; + int naddr; + int listed; + struct rblookup *lookup; +}; + +#define notlisted ((void*)1) + +static int nzones, nzalloc; +static const char **zones; + +static int do_txt; +static int stopfirst; +static int verbose = 1; +/* verbosity level: + * <0 - only bare As/TXTs + * 0 - what RBL result + * 1(default) - what is listed by RBL: result + * 2 - what is[not ]listed by RBL: result, name lookups + */ + +static int listed; +static int failures; + +static void *ecalloc(int size, int cnt) { + void *t = calloc(size, cnt); + if (!t) + error(1, "out of memory"); + return t; +} + +static void addzone(const char *zone) { + if (nzones >= nzalloc) { + const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16)); + if (zones) { + memcpy(zs, zones, nzones * sizeof(char*)); + free(zones); + } + zones = zs; + } + zones[nzones++] = zone; +} + +static int addzonefile(const char *fname) { + FILE *f = fopen(fname, "r"); + char linebuf[2048]; + if (!f) + return 0; + while(fgets(linebuf, sizeof(linebuf), f)) { + char *p = linebuf, *e; + while(*p == ' ' || *p == '\t') ++p; + if (*p == '#' || *p == '\n') continue; + e = p; + while(*e && *e != ' ' && *e != '\t' && *e != '\n') + ++e; + *e++ = '\0'; + p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup + addzone(p); + } + fclose(f); + return 1; +} + +static void dnserror(struct rblookup *ipl, const char *what) { + char buf[4*4]; + error(0, "unable to %s for %s (%s): %s", + what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)), + ipl->zone, dns_strerror(dns_status(0))); + ++failures; +} + +static void display_result(struct ipcheck *ipc) { + int j; + struct rblookup *l, *le; + char buf[4*4]; + if (!ipc->naddr) return; + for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) { + if (!l->addr) continue; + if (verbose < 2 && l->addr == notlisted) continue; + if (verbose >= 0) { + dns_ntop(AF_INET, &l->key, buf, sizeof(buf)); + if (ipc->name) printf("%s[%s]", ipc->name, buf); + else printf("%s", buf); + } + if (l->addr == notlisted) { + printf(" is NOT listed by %s\n", l->zone); + continue; + } + else if (verbose >= 1) + printf(" is listed by %s: ", l->zone); + else if (verbose >= 0) + printf(" %s ", l->zone); + if (verbose >= 1 || !do_txt) + for (j = 0; j < l->addr->dnsa4_nrr; ++j) + printf("%s%s", j ? " " : "", + dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf))); + if (!do_txt) ; + else if (l->txt) { + for(j = 0; j < l->txt->dnstxt_nrr; ++j) { + unsigned char *t = l->txt->dnstxt_txt[j].txt; + unsigned char *e = t + l->txt->dnstxt_txt[j].len; + printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : ""); + while(t < e) { + if (*t < ' ' || *t >= 127) printf("\\x%02x", *t); + else if (*t == '\\' || *t == '"') printf("\\%c", *t); + else putchar(*t); + ++t; + } + putchar('"'); + } + free(l->txt); + } + else + printf("%s", verbose > 0 ? "\n\t" : ""); + free(l->addr); + putchar('\n'); + } + free(ipc->lookup); +} + +static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) { + struct rblookup *ipl = data; + if (r) { + ipl->txt = r; + ++ipl->parent->listed; + } + else if (dns_status(ctx) != DNS_E_NXDOMAIN) + dnserror(ipl, "lookup DNSBL TXT record"); +} + +static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) { + struct rblookup *ipl = data; + if (r) { + ipl->addr = r; + ++listed; + if (do_txt) { + if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl)) + return; + dnserror(ipl, "submit DNSBL TXT record"); + } + ++ipl->parent->listed; + } + else if (dns_status(ctx) != DNS_E_NXDOMAIN) + dnserror(ipl, "lookup DNSBL A record"); + else + ipl->addr = notlisted; +} + +static int +submit_a_queries(struct ipcheck *ipc, + int naddr, const struct in_addr *addr) { + int z, a; + struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr); + ipc->lookup = rl; + ipc->naddr = naddr; + for(a = 0; a < naddr; ++a) { + for(z = 0; z < nzones; ++z) { + rl->key = addr[a]; + rl->zone = zones[z]; + rl->parent = ipc; + if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl)) + dnserror(rl, "submit DNSBL A query"); + ++rl; + } + } + return 0; +} + +static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) { + struct ipcheck *ipc = data; + if (rr) { + submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr); + free(rr); + } + else { + error(0, "unable to lookup `%s': %s", + ipc->name, dns_strerror(dns_status(ctx))); + ++failures; + } +} + +static int submit(struct ipcheck *ipc) { + struct in_addr addr; + if (dns_pton(AF_INET, ipc->name, &addr) > 0) { + submit_a_queries(ipc, 1, &addr); + ipc->name = NULL; + } + else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) { + error(0, "unable to submit name query for %s: %s\n", + ipc->name, dns_strerror(dns_status(0))); + ++failures; + } + return 0; +} + +static void waitdns(struct ipcheck *ipc) { + struct timeval tv; + fd_set fds; + int c; + int fd = dns_sock(NULL); + time_t now = 0; + FD_ZERO(&fds); + while((c = dns_timeouts(NULL, -1, now)) > 0) { + FD_SET(fd, &fds); + tv.tv_sec = c; + tv.tv_usec = 0; + c = select(fd+1, &fds, NULL, NULL, &tv); + now = time(NULL); + if (c > 0) + dns_ioevent(NULL, now); + if (stopfirst && ipc->listed) + break; + } +} + +int main(int argc, char **argv) { + int c; + struct ipcheck ipc; + char *nameserver = NULL; + int zgiven = 0; + + if (!(progname = strrchr(argv[0], '/'))) progname = argv[0]; + else argv[0] = ++progname; + + while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) { + case 's': ++zgiven; addzone(optarg); break; + case 'S': + ++zgiven; + if (addzonefile(optarg)) break; + error(1, "unable to read zonefile `%s'", optarg); + case 'c': ++zgiven; nzones = 0; break; + case 'q': --verbose; break; + case 'v': ++verbose; break; + case 't': do_txt = 1; break; + case 'n': nameserver = optarg; break; + case 'm': ++stopfirst; break; + case 'h': + printf("%s: %s (udns library version %s).\n", + progname, version, dns_version()); + printf("Usage is: %s [options] address..\n", progname); + printf( +"Where options are:\n" +" -h - print this help and exit\n" +" -s service - add the service (DNSBL zone) to the serice list\n" +" -S service-file - add the DNSBL zone(s) read from the given file\n" +" -c - clear service list\n" +" -v - increase verbosity level (more -vs => more verbose)\n" +" -q - decrease verbosity level (opposite of -v)\n" +" -t - obtain and print TXT records if any\n" +" -m - stop checking after first address match in any list\n" +" -n ipaddr - use the given nameserver instead of the default\n" +"(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n" +"or /etc/rblcheckrc in that order)\n" + ); + return 0; + default: + error(1, "use `%s -h' for help", progname); + } + + if (!zgiven) { + char *s = getenv("RBLCHECK_ZONES"); + if (s) { + char *k; + s = strdup(s); + for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t")) + addzone(k); + free(s); + } + else { /* probably worthless on windows? */ + char *path; + char *home = getenv("HOME"); + if (!home) home = "."; + path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc")); + sprintf(path, "%s/.rblcheckrc", home); + if (!addzonefile(path)) + addzonefile("/etc/rblcheckrc"); + free(path); + } + } + if (!nzones) + error(1, "no service (zone) list specified (-s or -S option)"); + + argv += optind; + argc -= optind; + + if (!argc) + return 0; + + if (dns_init(NULL, 0) < 0) + error(1, "unable to initialize DNS library: %s", strerror(errno)); + if (nameserver) { + dns_add_serv(NULL, NULL); + if (dns_add_serv(NULL, nameserver) < 0) + error(1, "wrong IP address for a nameserver: `%s'", nameserver); + } + if (dns_open(NULL) < 0) + error(1, "unable to initialize DNS library: %s", strerror(errno)); + + for (c = 0; c < argc; ++c) { + if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n'); + memset(&ipc, 0, sizeof(ipc)); + ipc.name = argv[c]; + submit(&ipc); + waitdns(&ipc); + display_result(&ipc); + if (stopfirst > 1 && listed) break; + } + + return listed ? 100 : failures ? 2 : 0; +} diff --git a/ami_test_client/libc-jss/udns/udns.3 b/ami_test_client/libc-jss/udns/udns.3 new file mode 100644 index 0000000000..23222aae9f --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns.3 @@ -0,0 +1,1352 @@ +.\" udns.3 +.\" udns library manpage +.\" +.\" Copyright (C) 2005-2014 Michael Tokarev +.\" This file is part of UDNS library, an async DNS stub resolver. +.\" +.\" This library is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU Lesser General Public +.\" License as published by the Free Software Foundation; either +.\" version 2.1 of the License, or (at your option) any later version. +.\" +.\" This library is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" Lesser General Public License for more details. +.\" +.\" You should have received a copy of the GNU Lesser General Public +.\" License along with this library, in file named COPYING.LGPL; if not, +.\" write to the Free Software Foundation, Inc., 59 Temple Place, +.\" Suite 330, Boston, MA 02111-1307 USA + +.TH udns 3 "Jan 2014" "Library Functions" + +.SH NAME +udns \- stub DNS resolver library + +.SH SYNOPSYS +.nf +#include +struct \fBdns_ctx\fR; +struct \fBdns_query\fR; +extern struct dns_ctx \fBdns_defctx\fR; +struct dns_ctx *\fIctx\fR; +typedef void \fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR); +typedef int +\fBdns_parse_fn\fR(const unsigned char *\fIqnd\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR, + void **\fIresultp\fR); + +\fBcc\fR ... -l\fBudns\fR +.fi + +.SH DESCRIPTION + +.PP +The DNS library, \fBudns\fR, implements thread-safe stub DNS resolver +functionality, which may be used both traditional, syncronous way +and asyncronously, with application-supplied event loop. + +.PP +While DNS works with both TCP and UDP, performing UDP query first and +if the result does not fit in UDP buffer (512 bytes max for original +DNS protocol), retrying the query over TCP, the library uses UDP only, +but uses EDNS0 (RFC2671) extensions which allows larger UDP buffers. + +.PP +The library uses single UDP socket to perform all operations even when +asking multiple nameservers. This way, it is very simple to use the +library in asyncronous event-loop applications: an application should +add only single socket to the set of filedescriptors it monitors for I/O. + +.PP +The library uses two main objects, \fIresolver context\fR of type +\fBstruct\ dns_ctx\fR, and \fIquery structure\fR of type +\fBstruct\ dns_query\fR, both are opaque for an application. +Resolver context holds global information about the resolver, +such as list of nameservers to use, list of active requests and the like. +Query objects holds information about a single DNS query in progress and +are allocated/processed/freed by the library. Pointer to query structure +may be treated as an identifier of an in-progress query and may be used +to cancel the asyncronous query or to wait for it to complete. + +.PP +Asyncronous interface works as follows. An application initializes +resolver context, submits any number of queries for it using one of +supplied \fBdns_submit_\fIXXX\fR() routines (each return the query +identifier as pointer to query structure), waits for input on the +UDP socket used by the library, and gives some control to the library +by calling \fBdns_ioevent\fR() and \fBdns_timeouts\fR() routines when +appropriate. The library performs all necessary processing and executes +application supplied callback routine when a query completes (either +successefully or not), giving it the result if any, pointer to the +resolver context (from which completion status may be obtained), and +the data pointer supplied by an application when the query has been +submitted. When submitting a query, an application requests how to +handle the reply -- to either return raw DNS reply packet for its +own low-level processing, or it may provide an address of \fIparsing +routine\fR of type \fBdns_parse_fn\fR to perform conversion of on-wire +format into easy to use data structure (the library provides parsing +routines for several commonly used resource record types, as well as +type-safe higher-level inteface that requests parsing automatically). +The I/O monitoring and timeout handling may be either traditional +select() or poll() based, or any callback-driven technique may be +used. + +.PP +Additionally, the library provides traditional syncronous interface, +which may be intermixed with asyncronous calls (during syncronous +query processing, other asyncronous queries for the same resolver +context continued to be processed as usual). An application uses +one of numerous \fBdns_resolve_\fIXXX\fR() routines provided by the +library to perform a query. As with asyncronous interface, an +application may either request to return raw DNS packet or type-specific +data structure by providing the parsing routine to handle the reply. +Every routine from \fBdns_resolve_\fIXXX\fR() series return pointer +to result or NULL in case of any error. Query completion status +(or length of the raw DNS packet) is available from the resolver +context using \fBdns_status\fR() routine, the same way as for the +asyncronous interface. + +.PP +Internally, library uses on-wire format of domain names, referred +to as \fIDN format\fR in this manual page. This is a series of domain +\fIlabels\fR whith preceeding length byte, terminated by zero-length +label wich is integral part of the DN format. There are several routines +provided to convert from traditional asciiz string to DN and back. +Higher-level type-specific query interface hides the DN format from +an application. + +.SH "COMMON DEFINITIONS" + +.PP +Every DNS Resource Record (RR) has a \fItype\fR and a \fIclass\fR. +The library defines several integer constants, \fBDNS_C_\fIXXX\fR and +\fBDNS_T_\fIXXX\fR, to use as symbolic names for RR classes and types, +such as \fBDNS_C_IN\fR for Internet class, \fBDNS_T_A\fR for IPv4 +address record type and so on. See udns.h header file for complete list +of all such constants. + +.PP +The following constants are defined in udns.h header file: +.IP "\fBDNS_MAXDN\fR (255 bytes)" +Maximum length of the domain name in internal (on-wire) DN format. +.IP "\fBDNS_MAXLABEL\fR (63 bytes)" +Maximum length of a single label in DN format. +.IP "\fBDNS_MAXNAME\fR (1024 bytes)" +Maximum length of asciiz format of a domain name. +.IP "\fBDNS_HSIZE\fR (12 bytes)" +Size of header in DNS packet. +.IP "\fBDNS_PORT\fR (53)" +Default port to use when contacting a DNS server. +.IP "\fBDNS_MAXSERV\fR (6 servers)" +Maximum number of DNS servers to use. +.IP "\fBDNS_MAXPACKET\fR (512 bytes)" +Maximum length of DNS UDP packet as specified by original DNS protocol +.IP "\fBDNS_EDNS0PACKET\fR (4096 bytes)" +Default length of DNS UDP packet (with EDNS0 extensions) the library uses. +Note that recursive nameservers usually resides near the client asking them +to resolve names, e.g. on the same LAN segment or even on the same host, so +UDP packet fragmentation isn't a problem in most cases. Note also that +the size of actual packets will be as many bytes as actual reply size requires, +which is smaller than this value in almost all cases. + +.PP +Additionally, several constants are defined to simplify work with raw DNS +packets, such as DNS response codes (\fBDNS_R_\fIXXX\fR), DNS header layout +(\fBDNS_H_\fIXXX\fR) and others. Again, see udns.h for complete list. +Library error codes (\fBDNS_E_\fIXXX\fR) are described later in this +manual page. + +.SH "RESOLVER CONTEXT" + +.PP +Resolver context, of type \fBstruct\ dns_ctx\fR, is an object which is +opaque to an application. Several routines provided by the library +to initialize, copy and free resolver contexts. Most other high-level +routines in this library expects a pointer to resolver context, \fIctx\fR, +as the first argument. There is a default resolver context available, +named \fBdns_defctx\fR. When the context pointer \fIctx\fR passed to +a routine is NULL, \fBdns_defctx\fR is used. Several resolver contexts +may be active at the same time, for example, when an application is +multi-threaded and each thread uses resolver. +.PP +In order to use the library, an application should initialize and open +one or more resolver context objects. These are two separate actions, +performed by \fBdns_init\fR() (or \fBdns_reset\fR()), and \fBdns_open\fR(). +Between the two calls, an application is free to pefrorm additional +initialisation, such as setting custom nameservers, options or domain search +lists. Optionally, in case no additional custom initialisation is required, +\fBdns_init\fR() may open the context if \fIdo_open\fR argument (see below) +is non-zero. +.PP +When initializing resolver context, the library uses information from +system file /etc/resolv.conf (see \fBresolv.conf\fR(5)), consults +environment variables \fB$LOCALDOMAIN\fR, \fB$NSCACHEIP\fR, +\fB$NAMESERVERS\fR and \fB$RES_OPTIONS\fR, and local host name to obtain +list of local nameservers, domain name search list and various resolver +options. +.PP +The following routines to initialize resolver context are available: +.PP +.nf +void \fBdns_reset\fR(\fIctx\fR) +int \fBdns_init\fR(\fIctx\fR, int \fIdo_open\fR) +.fi +.RS +\fBdns_reset\fR() resets a given resolver context to default values, +preparing it to be opened by \fBdns_open\fR(). +It is ok to call this routine against opened and active context - all active +queries will be dropped, sockets will be closed and so on. This routine +does not initialize any parameters from system configuration files, use +\fBdns_init\fR() for this. There's no error return - operation always +succeeds. \fBdns_init\fR() does everything \fBdns_reset\fR() does, +plus initializes various parameters of the context according to system +configuration and process environment variables. If \fIdo_open\fR is +non-zero, \fBdns_init\fR() calls \fIdns_open\fR(), so that the whole +library initialisation is performed in a single step. +.RE +.PP +.nf +struct dns_ctx *\fBdns_new\fR(struct dns_ctx *\fIcopy\fR) +void \fBdns_free\fR(\fIctx\fR) +.fi +.RS +\fBdns_new\fR() allocates new resolver context and copies all parameters +for a given resolver context \fIcopy\fR, or default context if \fIcopy\fR +is NULL, and returns pointer to the newly allocated context. The context +being copied should be initialized. +\fBdns_new\fR() may fail if there's no memory available to make a copy +of \fIcopy\fR, in which case the routine will return NULL pointer. +\fBdns_free\fR() is used to close assotiated socket and free resolver +context resources and cancelling (abandoming) all active queries +assotiated with it. It's an error to free \fBdns_defctx\fR, only +dynamically allocated contexts returned by \fBdns_new\fR() are allowed +to be freed by \fBdns_free\fR(). +.RE +.PP +.nf +int \fBdns_add_serv\fR(\fIctx\fR, const char *\fIservaddr\fR) +int \fBdns_add_serv_s\fR(\fIctx\fR, const struct sockaddr *\fIsa\fR) +int \fBdns_add_srch\fR(\fIctx\fR, const char *\fIsrch\fR) +.fi +.RS +Add an element to list of nameservers (\fBdns_add_serv\fR(), as +asciiz-string \fIservaddr\fR with an IP address of the nameserver, +and \fBdns_add_serv_s\fR(), as initialized socket address \fIsa\fR), +or search list (\fBdns_add_srch\fR(), as a pointer to domain name) +for the given context \fIctx\fR. If the last argument is a NULL +pointer, the corresponding list (search or nameserver) is reset +instead. Upon successeful completion, each routine returns new +number of elements in the list in question. On error, negative +value is returned and global variable \fBerrno\fR is set appropriately. +It is an error to call any of this functions if the context is +opened (after \fBdns_open\fR() or \fBdns_init\fR() with non-zero argument). +.RE +.PP +.nf +int \fBdns_set_opts\fR(\fIctx\fR, const char *\fIopts\fR) +.fi +.RS +set resolver context options from \fIopts\fR string, in the same way as +processing \fBoptions\fR statement in resolv.conf and \fB$RES_OPTIONS\fR +environment variable. Return number of unrecognized/invalid options +found (all recognized and valid options gets processed). +.RE +.PP +.nf +void \fBdns_set_opt\fR(\fIctx\fR, int \fIopt\fR, \fIval\fR) +.fi +.RS +.B TODO +The \fIflags\fR argument is a bitmask with the following bits defined: +.IP \fBDNS_NOSRCH\fR +do not perform domain name search in search list. +.IP \fBDNS_NORD\fR +do not request recursion when performing queries +(i.e. don't set RD flag in querues). +.IP \fBDNS_AAONLY\fR +request authoritative answers only (i.e. set AA +flag in queries). +.RE + +.PP +.nf +int \fBdns_open\fR(\fIctx\fR) +int \fBdns_sock\fR(const \fIctx\fR) +void \fBdns_close\fR(\fIctx\fR) +.fi +.RS +\fBdns_open\fR() opens the UDP socket used for queries if not already +open, and return assotiated filedescriptor (or negative value in case +of error). Before any query can be submitted, the context should be +opened using this routine. And before opening, the context should be +initialized. +\fBdns_sock\fR() return the UDP socket if open, or -1 if not. +\fBdns_close\fR() closes the UDP socket if it was open, and drops all active +queries if any. +.RE + +.PP +.nf +int \fBdns_active\fR(const \fIctx\fR) +.fi +.RS +return number of active queries queued for the given context +\fIctx\fR, or zero if none. +.RE + +.PP +.nf +int \fBdns_status\fR(const \fIctx\fR) +.fi +.RS +return status code from last operation. When using syncronous +interface, this is the query completion status of the last query. +With asyncronous interface, from within the callback routine, +this is the query completion status of the query for which the +callback is being called. When query submission fails, this +is the error code indicating failure reason. All error codes +are negative and are represented by \fBDNS_E_\fIXXX\fR constants +described below. +.RE + +.PP +.nf +void \fBdns_ioevent\fR(\fIctx\fR, time_t \fInow\fR) +.fi +.RS +this routine may be called by an application to process I/O +events on the UDP socket used by the library, as returned +by \fBdns_sock\fR(). The routine tries to receive incoming +UDP datagram from the socket and process it. The socket is +set up to be non-blocking, so it is safe to call the routine +even if there's no data to read. The routine will process +as many datagrams as are queued for the socket, so it is +safe to use it with either level-triggered or edge-triggered +I/O monitoring model. The \fInow\fR argument is either a +current time as returned by \fBtime\fR(), or 0, in which +case the routine will obtain current time by it's own. +.RE + +.PP +.nf +int \fBdns_timeouts\fR(\fIctx\fR, int \fImaxwait\fR, time_t \fInow\fR) +.fi +.RS +process any pending timeouts and return number of secounds +from current time (\fInow\fR if it is not 0) to the time when +the library wants the application to pass it control to process +more queued requests. In case when there are no requests pending, +this time is -1. The routine will not request a time larger than +\fImaxwait\fR secounds if it is greather or equal to zero. If +\fInow\fR is 0, the routine will obtain current time by it's own; +when it is not 0, it should contain current time as returned by +\fBtime\fR(). +.RE + +.PP +.nf +typedef void \fBdns_utm_fn\fR(\fIctx\fR, int \fItimeout\fR, void *\fIdata\fR) +void \fBdns_set_tmcbck\fR(\fIctx\fR, dns_utm_fn *\fIutmfn\fR, void *\fIdata\fR) +.fi +.RS +An application may use custom callback-based I/O multiplexing mechanism. +Usually such a mechanism have concept of a \fItimer\fR, and an ability +to register a timer event in a form of a callback routine which will +be executed after certain amount of time. In order to use such an +event mechanism, udns provides an ability to register and de-register +timer events necessary for internal processing using whatever event +mechanism an application uses. For this to work, it is possible to +assotiate a pointer to a routine that will perform necessary work for +(de)registering timer events with a given resolver context, and +udns will call that routine at appropriate times. Prototype of +such a routine is shown by \fBdns_utm_fn\fR typedef above. Libudns +assotiates single timer with resolver context. User-supplied \fIutmfn\fR +routine will be called by the library with the following arguments: +.IP "\fIctx\fR == NULL" +delete user timer, at context free time or when an application changes +user timer request routine using \fBdns_set_tmcbck\fR(); +.IP "\fIctx\fR != NULL, \fItimeout\fR < 0" +don't fire timer anymore, when there are no active requests; +.IP "\fIctx\fR != NULL, \fItimeout\fR == 0" +fire timer at the next possibility, but not immediately; +.IP "\fIctx\fR != NULL, \fItimeout\fR > 0" +fire timer after \fItimeout\fR seconds after now. +.PP +The \fIdata\fR argument passed to the routine will be the same +as passed to \fBdns_set_tmcbck\fR(). +.PP +When a timer expires, an application should call \fBdns_timeouts\fR() +routine (see below). Non-callback timer usage is provided too. +.RE + +.PP +.B XXXX TODO: some more resolver context routines, like dns_set_dbgfn() etc. + +.SH "QUERY INTERFACE" + +.PP +There are two ways to perform DNS queries: traditional syncronous +way, when udns performs all the necessary processing and return +control to the application only when the query completes, and +asyncronous way, when an application submits one or more queries +to the library using given resolver context, and waits for completion +by monitoring filedescriptor used by library and calling library +routines to process input on that filedescriptor. Asyncronous mode +works with callback routines: an application supplies an address of +a routine to execute when the query completes, and a data pointer, +which is passed to the callback routine. + +.PP +Queries are submitted to the library in a form of \fBstruct\ dns_query\fR. +To perform asyncronous query, an application calls one of the +\fBdns_submit_\fIXXX\fR() rounines, and provides necessary information +for a callback, together with all the query parameters. +When the query completes, library will call application-supplied callback +routine, giving it the resolver context (wich holds query completion status), +dynamically allocated result (which will be either raw DNS packet or, if +applicatin requested parsing the result by specifying non-NULL parse routine, +ready-to-use type-specific structure), and a data pointer provided by an +application when it submitted the query. It is the application who's +responsible for freeing the result memory. +.PP +Generic query callback routine looks like this: +.nf +typedef void +\fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR) +.fi +Type-specific query interface expects similar form of callback +routine with the only difference in type of \fBresult\fR argument, +which will be pointer to specific data structure (decoded reply) +instead of this void pointer to raw DNS packet data. + +.PP +Result parsing routine looks like this: +.nf +typedef int +\fBdns_parse_fn\fR(const unsigned char *\fIqdn\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR, + void **\fIresultp\fR); +.fi +When called by the library, the arguments are as follows: +\fIpkt\fR points to the start of the packet received; +\fIend\fR points past the end of the packet received; +\fIcur\fR points past the query DN in the query section of the +packet; +\fIqdn\fR points to the original query DN. +The routine should allocate a single buffer to hold the result, +parse the reply filling in the buffer, and return the buffer +using \fIresultp\fR argument. It returns 0 in case of error, +or udns error code (\fBDNS_E_\fIXXX\fR constants) in case of +error. +Note that by the time when the parse routine is called by the +library, packet is already verified to be a reply to the +original query, by matching query DN, query class and query type. + +.PP +Type-specific query inteface supplies necessary parsing routines +automatically. + +.PP +In case of error, query completion status as returned by +\fBdns_status\fR(\fIctx\fR), will contain one of the following values: +.IP "positive value" +length of raw DNS packet if parsing is not requested. +.IP 0 +the query was successeful and the \fIreply\fR points to type-specific +data structure. +.IP \fBDNS_E_TEMPFAIL\fR +temporary error, the resolver nameserver was not able to +process our query or timed out. +.IP \fBDNS_E_PROTOCOL\fR +protocol error, a nameserver returned malformed reply. +.IP \fBDNS_E_NXDOMAIN\fR +the domain name does not exist. +.IP \fBDNS_E_NODATA\fR +there is no data of requested type found. +.IP \fBDNS_E_NOMEM\fR +out of memory while processing request. +.IP \fBDNS_E_BADQUERY\fR +some aspect of the query (most common is the domain name in question) +is invalid, and the library can't even start a query. + +.PP +Library provides two series of routines which uses similar interface -- +one for asyncronous queries and another for syncronous queries. There +are two general low-level routines in each series to submit (asyncronous +interface) and resolve (syncronous interface) queries, as well as several +type-specific routines with more easy-to-use interfaces. To submit +an asyncronous query, use one of \fBdns_submit_\fIXXX\fR() routine, each +of which accepts query parameters, pointers to callback routine and to +callback data, and optional current time hint. Note type-specific +\fBdns_submit_\fIXXX\fR() routines expects specific type of the callback +routine as well, which accepts reply as a pointer to corresponding +structure, not a void pointer). Every \fBdns_submit_\fIXXX\fR() routine +return pointer to internal query structure of type struct\ dns_query, +used as an identifier for the given query. + +.PP +To resolve a query syncronously, use one of \fBdns_resolve_\fIXXX\fR() +routines, which accepts the same query parameters (but not the +callback pointers) as corresponding \fBdns_submit_\fIXXX\fR(), and +return the query result, which is the same as passed to the callback +routine in case of asyncronous interface. + +.PP +In either case, the result memory (if the query completed successefully) +is dynamically allocated and should be freed by an application. If +the query failed for any reason, the result will be NULL, and error +status will be available from \fBdns_status\fR(\fIctx\fR) routine +as shown above. + +.PP +.nf +struct dns_query * +\fBdns_submit_dn\fR(\fIctx\fR, + const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, + \fIparse\fR, \fIcbck\fR, \fIdata\fR) +struct dns_query * +\fBdns_submit_p\fR(\fIctx\fR, + const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, + \fIparse\fR, \fIcbck\fR, \fIdata\fR) + enum dns_class \fIqcls\fR; + enum dns_type \fIqtyp\fR; + int \fIflags\fR; + dns_parse_fn *\fIparse\fR; + dns_query_fn *\fIcbck\fR; + void *\fIdata\fR; +.fi +.RS +submit a query for processing for the given resolver context \fIctx\fR. +Two routines differs only in 3rd argument, which is domain name in +DN format (\fIdn\fR) or asciiz string (\fIname\fR). The query will be +performed for the given domain name, with type \fIqtyp\fR in class \fIqcls\fR, +using option bits in \fIflags\fR, using RR parsing routine pointed by +\fIparse\fR if not-NULL, and upon completion, \fIcbck\fR function will +be called with the \fIdata\fR argument. +In case of successeful query submission, +the routine return pointer to internal query structure which may be treated +as an identifier of the query as used by the library, and may be used as an +argument for \fBdns_cancel\fR() routine. In case of error, NULL will be +returned, and context error status (available using \fIdns_status\fR() routine) +will be set to corresponding error code, which in this case may be +DNS_E_BADQUERY if the \fIname\fR of \fIdn\fR is invalid, DNS_E_NOMEM if +there's no memory available to allocate query structure, or DNS_E_TEMPFAIL +if an internal error occured. +.RE + +.PP +.nf +void *\fBdns_resolve_dn\fR(\fIctx\fR, + const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR); +void *\fBdns_resolve_p\fR(\fIctx\fR, + const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR) + enum dns_class \fIqcls\fR; + enum dns_type \fIqtyp\fR; + int \fIflags\fR; + dns_parse_fn *\fIparse\fR; +.fi +.RS +syncronous interface. The routines perform all the steps necessary to resolve +the given query and return the result. If there's no positive result for any +reason, all the routines return NULL, and set context error status (available +using \fBdns_status\fR() routine) to indicate the error code. If the query +was successeful, context status code will contain either the length of the +raw DNS reply packet if \fIparse\fR argument was NULL (in which case the return +value is pointer to the reply DNS packet), or 0 (in which case the return value +is the result of \fIparse\fR routine). If the query successeful (return value +is not NULL), the memory returned was dynamically allocated by the library +and should be free()d by application after use. +.RE + +.PP +.nf +void *\fBdns_resolve\fR(\fIctx\fR, struct dns_query *\fIq\fR) +.fi +.RS +wait for the given query \fIq\fR, as returned by one of +\fBdns_submit_\fIXXX\fR() routines, for completion, and +return the result. The callback routine will not be called +for this query. After completion, the query identifier \fIq\fR +is not valid. Both \fBdns_resolve_dn\fR() and \fBdns_resolve_p\fR() +are just wrappers around corresponding submit routines and this +\fBdns_resolve\fR() routine. +.RE + +.PP +.nf +void \fBdns_cancel\fR(\fIctx\fR, struct dns_query *\fIq\fR) +.fi +.RS +cancel an active query \fIq\fR, without calling a callback routine. +After completion, the query identifier \fIq\fR is not valid. +.RE + +.SH "TYPE-SPECIFIC QUERIES" + +.PP +In addition to the generic low-level query interface, the library provides +a set of routines to perform specific queries in a type-safe manner, as +well as parsers for several well-known resource record types. The library +implements high-level interface for A, AAAA, PTR, MX and TXT records +and DNSBL and RHSBL functionality. These routines returns specific types +as result of a query, instead of raw DNS packets. The following types +and routines are available. + +.PP +.nf +struct \fBdns_rr_null\fR { + char *\fBdnsn_qname\fR; /* original query name */ + char *\fBdnsn_cname\fR; /* canonical name */ + unsigned \fBdnsn_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsn_nrr\fR; /* number of records in the set */ +}; +.fi +.PP +NULL RR set, used as a base for all other RR type structures. +Every RR structure as used by the library have four standard +fields as in struct\ \fBdns_rr_null\fR. + +.SS "IN A Queries" +.PP +.nf +struct \fBdns_rr_a4\fR { /* IN A RRset */ + char *\fBdnsa4_qname\fR; /* original query name */ + char *\fBdnsa4_cname\fR; /* canonical name */ + unsigned \fBdnsa4_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsa4_nrr\fR; /* number of addresses in the set */ + struct in_addr \fBdnsa4_addr\fR[]; /* array of addresses */ +}; +typedef void + \fBdns_query_a4_fn\fR(\fIctx\fR, struct dns_rr_a4 *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_a4\fB; +struct dns_query * +\fBdns_submit_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_a4_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_a4 * +\fBdns_resolve_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_a4\fR structure holds a result of an \fBIN A\fR query, +which is an array of IPv4 addresses. Callback routine for IN A queries +expected to be of type \fBdns_query_a4_fn\fR, which expects pointer to +\fBdns_rr_a4\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_a4\fR() is used to convert raw DNS reply packet into +\fBdns_rr_a4\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_a4\fR() and +\fBdns_resolve_a4\fR() are used to perform A IN queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "IN AAAA Queries" +.PP +.nf +struct \fBdns_rr_a6\fR { /* IN AAAA RRset */ + char *\fBdnsa6_qname\fR; /* original query name */ + char *\fBdnsa6_cname\fR; /* canonical name */ + unsigned \fBdnsa6_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsa6_nrr\fR; /* number of addresses in the set */ + struct in6_addr \fBdnsa6_addr\fR[]; /* array of addresses */ +}; +typedef void + \fBdns_query_a6_fn\fR(\fIctx\fR, struct dns_rr_a6 *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_a6\fB; +struct dns_query * +\fBdns_submit_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_a6_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_a6 * +\fBdns_resolve_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_a6\fR structure holds a result of an \fBIN AAAA\fR query, +which is an array of IPv6 addresses. Callback routine for IN AAAA queries +expected to be of type \fBdns_query_a6_fn\fR, which expects pointer to +\fBdns_rr_a6\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_a6\fR() is used to convert raw DNS reply packet into +\fBdns_rr_a6\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_a6\fR() and +\fBdns_resolve_a6\fR() are used to perform AAAA IN queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "IN PTR Queries" +.PP +.nf +struct \fBdns_rr_ptr\fR { /* IN PTR RRset */ + char *\fBdnsptr_qname\fR; /* original query name */ + char *\fBdnsptr_cname\fR; /* canonical name */ + unsigned \fBdnsptr_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsptr_nrr\fR; /* number of domain name pointers */ + char *\fBdnsptr_ptr\fR[]; /* array of domain name pointers */ +}; +typedef void + \fBdns_query_ptr_fn\fR(\fIctx\fR, struct dns_rr_ptr *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_ptr\fB; +struct dns_query * +\fBdns_submit_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR, + dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_ptr * +\fBdns_resolve_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR); +struct dns_query * +\fBdns_submit_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR, + dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_ptr * +\fBdns_resolve_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR); +.fi +.PP +The \fBdns_rr_ptr\fR structure holds a result of an IN PTR query, which +is an array of domain name pointers for a given IPv4 or IPv6 address. +Callback routine for IN PTR queries expected to be of type +\fBdns_query_ptr_fn\fR, which expects pointer to \fBdns_rr_ptr\fR +structure as query result instead of raw DNS packet. The \fBdns_parse_ptr\fR() +is used to convert raw DNS reply packet into \fBdns_rr_ptr\fR structure +(it is used internally and may be used directly too with generic query +interface). Routines \fBdns_submit_a4ptr\fR() and \fBdns_resolve_a4ptr\fR() +are used to perform IN PTR queries for IPv4 addresses in a type-safe +manner. Routines \fBdns_submit_a6ptr\fR() and \fBdns_resolve_a6ptr\fR() +are used to perform IN PTR queries for IPv6 addresses. + +.SS "IN MX Queries" +.PP +.nf +struct \fBdns_mx\fR { /* single MX record */ + int \fBpriority\fR; /* priority value of this MX */ + char *\fBname\fR; /* domain name of this MX */ +}; +struct \fBdns_rr_mx\fR { /* IN MX RRset */ + char *\fBdnsmx_qname\fR; /* original query name */ + char *\fBdnsmx_cname\fR; /* canonical name */ + unsigned \fBdnsmx_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsmx_nrr\fR; /* number of mail exchangers in the set */ + struct dns_mx \fBdnsmx_mx\fR[]; /* array of mail exchangers */ +}; +typedef void + \fBdns_query_mx_fn\fR(\fIctx\fR, struct dns_rr_mx *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_mx\fB; +struct dns_query * +\fBdns_submit_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_mx_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_mx * +\fBdns_resolve_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_mx\fR structure holds a result of an IN MX query, which +is an array of mail exchangers for a given domain. Callback routine for IN MX +queries expected to be of type \fBdns_query_mx_fn\fR, which expects pointer to +\fBdns_rr_mx\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_mx\fR() is used to convert raw DNS reply packet into +\fBdns_rr_mx\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_mx\fR() and +\fBdns_resolve_mx\fR() are used to perform IN MX queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "TXT Queries" +.PP +.nf +struct \fBdns_txt\fR { /* single TXT record */ + int \fBlen\fR; /* length of the text */ + unsigned char *\fBtxt\fR; /* pointer to the text */ +}; +struct \fBdns_rr_txt\fR { /* TXT RRset */ + char *\fBdnstxt_qname\fR; /* original query name */ + char *\fBdnstxt_cname\fR; /* canonical name */ + unsigned \fBdnstxt_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnstxt_nrr\fR; /* number of text records in the set */ + struct dns_txt \fBdnstxt_txt\fR[]; /* array of TXT records */ +}; +typedef void + \fBdns_query_txt_fn\fR(\fIctx\fR, struct dns_rr_txt *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_txt\fB; +struct dns_query * +\fBdns_submit_txt\fB(\fIctx\fR, const char *\fIname\fR, enum dns_class \fIqcls\fR, + int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_txt * +\fBdns_resolve_txt\fB(\fIctx\fR, const char *\fIname\fR, + enum dns_class \fIqcls\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_txt\fR structure holds a result of a TXT query, which is an +array of text records for a given domain name. Callback routine for TXT +queries expected to be of type \fBdns_query_txt_fn\fR, which expects pointer +to \fBdns_rr_txt\fR structure as query result instead of raw DNS packet. +The \fBdns_parse_txt\fR() is used to convert raw DNS reply packet into +\fBdns_rr_txt\fR structure (it is used internally and may be used directly too +with generic query interface). Routines \fBdns_submit_txt\fR() and +\fBdns_resolve_txt\fR() are used to perform IN MX queries in a type-safe +manner. The \fIname\fR parameter is the domain name in question, and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). Note that each TXT string +is represented by \fBstruct\ dns_txt\fR, while zero-terminated (and the +len field of the structure does not include the terminator), may contain +embedded null characters -- content of TXT records is not interpreted +by the library in any way. + +.SS "SRV Queries" +.PP +.nf +struct \fBdns_srv\fR { /* single SRV record */ + int \fBpriority\fR; /* priority of the record */ + int \fBweight\fR; /* weight of the record */ + int \fBport\fR; /* the port number to connect to */ + char *\fBname\fR; /* target host name */ +}; +struct \fBdns_rr_srv\fR { /* SRV RRset */ + char *\fBdnssrv_qname\fR; /* original query name */ + char *\fBdnssrv_cname\fR; /* canonical name */ + unsigned \fBdnssrv_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnssrv_nrr\fR; /* number of text records in the set */ + struct dns_srv \fBdnssrv_srv\fR[]; /* array of SRV records */ +}; +typedef void + \fBdns_query_srv_fn\fR(\fIctx\fR, struct dns_rr_srv *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_srv\fB; +struct dns_query * +\fBdns_submit_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, + int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_srv * +\fBdns_resolve_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR, + int \fIflags\fR); +.fi +.PP +The \fBdns_rr_srv\fR structure holds a result of an IN SRV (rfc2782) query, +which is an array of servers (together with port numbers) which are performing +operations for a given \fIservice\fR using given \fIprotocol\fR on a target +domain \fIname\fR. Callback routine for IN SRV queries expected to be of type +\fBdns_query_srv_fn\fR, which expects pointer to \fBdns_rr_srv\fR structure as +query result instead of raw DNS packet. The \fBdns_parse_srv\fR() is used to +convert raw DNS reply packet into \fBdns_rr_srv\fR structure (it is used +internally and may be used directly too with generic query interface). +Routines \fBdns_submit_srv\fR() and \fBdns_resolve_srv\fR() are used to +perform IN SRV queries in a type-safe manner. The \fIname\fR parameter +is the domain name in question, \fIservice\fR and \fRprotocl\fR specifies the +service and the protocol in question (the library will construct query DN +according to rfc2782 rules) and may be NULL (in this case the library +assumes \fIname\fR parameter holds the complete SRV query), and +\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical +interest (if the \fIname\fR is absolute, that is, it ends up with a dot, +DNS_NOSRCH flag will be set automatically). + +.SS "NAPTR Queries" +.PP +.nf +struct \fBdns_naptr\fR { /* single NAPTR record */ + int \fBorder\fR; /* record order */ + int \fBpreference\fR; /* preference of this record */ + char *\fBflags\fR; /* application-specific flags */ + char *\fBservice\fR; /* service parameter */ + char *\fBregexp\fR; /* substitutional regular expression */ + char *\fBreplacement\fR; /* replacement string */ +}; +struct \fBdns_rr_naptr\fR { /* NAPTR RRset */ + char *\fBdnsnaptr_qname\fR; /* original query name */ + char *\fBdnsnaptr_cname\fR; /* canonical name */ + unsigned \fBdnsnaptr_ttl\fR; /* Time-To-Live (TTL) value */ + int \fBdnsnaptr_nrr\fR; /* number of text records in the set */ + struct dns_naptr \fBdnsnaptr_naptr\fR[]; /* array of NAPTR records */ +}; +typedef void + \fBdns_query_naptr_fn\fR(\fIctx\fR, struct dns_rr_naptr *\fIresult\fR, \fIdata\fR) +dns_parse_fn \fBdns_parse_naptr\fB; +struct dns_query * +\fBdns_submit_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR, + dns_query_txt_fn *\fIcbck\fR, \fIdata\fR); +struct dns_rr_naptr * +\fBdns_resolve_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR); +.fi +.PP +The \fBdns_rr_naptr\fR structure holds a result of an IN NAPTR (rfc3403) query. +Callback routine for IN NAPTR queries expected to be of type +\fBdns_query_naptr_fn\fR, expects pointer to \fBdns_rr_naptr\fR +structure as query result instead of raw DNS packet. +The \fBdns_parse_naptr\fR() is used to convert raw DNS reply packet into +\fBdns_rr_naptr\fR structure (it is used +internally and may be used directly too with generic query interface). +Routines \fBdns_submit_naptr\fR() and \fBdns_resolve_naptr\fR() are used to +perform IN NAPTR queries in a type-safe manner. The \fIname\fR parameter +is the domain name in question, and \fIflags\fR is query flags bitmask, +with one bit, DNS_NOSRCH, of practical interest (if the \fIname\fR is +absolute, that is, it ends up with a dot, DNS_NOSRCH flag will be set +automatically). + +.SS "DNSBL Interface" +.PP +A DNS-based blocklists, or a DNSBLs, are in wide use nowadays, especially +to protect mailservers from spammers. The library provides DNSBL interface, +a set of routines to perform queries against DNSBLs. Routines accepts an +IP address (IPv4 and IPv6 are both supported) and a base DNSBL zone as +query parameters, and returns either \fBdns_rr_a4\fR or \fBdns_rr_txt\fR +structure. Note that IPv6 interface return IPv4 RRset. +.PP +.nf +struct dns_query * +\fBdns_submit_a4dnsbl\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a4dnsbl_txt\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a6dnsbl\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_a6dnsbl_txt\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_rr_a4 *\fBdns_resolve_a4dnsbl\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_txt *\fBdns_resolve_a4dnsbl_txt\fR(\fIctx\fR, + const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_a4 *\fBdns_resolve_a6dnsbl\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +struct dns_rr_txt *\fBdns_resolve_a6dnsbl_txt\fR(\fIctx\fR, + const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR) +.fi +Perform (submit or resolve) a DNSBL query for the given \fIdnsbl\fR +domain and an IP \fIaddr\fR in question, requesting either A or TXT +records. + +.SS "RHSBL Interface" +.PP +RHSBL is similar to DNSBL, but instead of an IP address, the +parameter is a domain name. +.PP +.nf +struct dns_query * +\fBdns_submit_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, + dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_query * +\fBdns_submit_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR, + dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR); +struct dns_rr_a4 * +\fBdns_resolve_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); +struct dns_rr_txt * +\fBdns_resolve_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR); +.fi +Perform (submit or resolve) a RHSBL query for the given \fIrhsbl\fR +domain and \fIname\fR in question, requesting either A or TXT records. + + +.SH "LOW-LEVEL INTERFACE" + +.SS "Domain Names (DNs)" + +.PP +A DN is a series of domain name labels each starts with length byte, +followed by empty label (label with zero length). The following +routines to work with DNs are provided. + +.PP +.nf +unsigned \fBdns_dnlen\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return length of the domain name \fIdn\fR, including the terminating label. +.RE + +.PP +.nf +unsigned \fBdns_dnlabels\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return number of non-zero labels in domain name \fIdn\fR. +.RE + +.PP +.nf +unsigned \fBdns_dnequal\fR(\fIdn1\fR, \fIdn2\fR) + const unsigned char *\fIdn1\fR, *\fIdn2\fR; +.fi +.RS +test whenever the two domain names, \fIdn1\fR and \fIdn2\fR, are +equal (case-insensitive). Return domain name length if equal +or 0 if not. +.RE + +.PP +.nf +unsigned \fBdns_dntodn\fR(\fIsdn\fR, \fIddn\fR, \fIdnsiz\fR) + const unsigned char *\fIsdn\fR; + unsigned char *\fIddn\fR; + unsigned \fIdnsiz\fR; +.fi +.RS +copies the source domain name \fIsdn\fR to destination buffer \fIddn\fR +of size \fIdnsiz\fR. Return domain name length or 0 if \fIddn\fR is +too small. +.RE + +.PP +.nf +int \fBdns_ptodn\fR(\fIname\fR, \fInamelen\fR, \fIdn\fR, \fIdnsiz\fR, \fIisabs\fR) +int \fBdns_sptodn\fR(\fIname\fR, \fIdn\fR, \fIdnsiz\fR) + const char *\fIname\fR; unsigned \fInamelen\fR; + unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; + int *\fIisabs\fR; +.fi +.RS +convert asciiz name \fIname\fR of length \fInamelen\fR to DN format, +placing result into buffer \fIdn\fR of size \fIdnsiz\fR. Return +length of the DN if successeful, 0 if the \fIdn\fR buffer supplied is +too small, or negative value if \fIname\fR is invalid. If \fIisabs\fR +is non-NULL and conversion was successeful, *\fIisabs\fR will be set to +either 1 or 0 depending whenever \fIname\fR was absolute (i.e. ending with +a dot) or not. Name length, \fInamelength\fR, may be zero, in which case +strlen(\fIname\fR) will be used. Second form, \fBdns_sptodn\fR(), is a +simplified form of \fBdns_ptodn\fR(), equivalent to +.br +.nf +\fBdns_ptodn\fR(\fIname\fR, 0, \fIdn\fR, \fIdnlen\fR, 0). +.fi +.RE + +.PP +.nf +extern const unsigned char \fBdns_inaddr_arpa_dn\fR[] +int \fBdns_a4todn\fR(const struct in_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +int \fBdns_a4ptodn\fR(const struct in_addr *\fIaddr\fR, const char *\fItname\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +extern const unsigned char \fBdns_ip6_arpa_dn\fR[] +int \fBdns_a6todn\fR(const struct in6_addr *\fIaddr\fR, const unsigned char *\fItdn\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +int \fBdns_a6ptodn\fR(const struct in6_addr *\fIaddr\fR, const char *\fItname\fR, + unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR) +.fi +.RS +several variants of routines to convert IPv4 and IPv6 address \fIaddr\fR +into reverseDNS-like domain name in DN format, storing result in \fIdn\fR +of size \fIdnsiz\fR. \fItdn\fR (or \fItname\fR) is the base zone name, +like in-addr.arpa for IPv4 or in6.arpa for IPv6. If \fItdn\fR (or \fItname\fR) +is NULL, \fBdns_inaddr_arpa_dn\fR (or \fBdns_ip6_arpa_dn\fR) will be used. +The routines may be used to construct a DN for a DNSBL lookup for example. +All routines return length of the resulting DN on success, -1 if resulting +DN is invalid, or 0 if the \fIdn\fR buffer (\fIdnsiz\fR) is too small. +To hold standard rDNS DN, a buffer of size \fBDNS_A4RSIZE\fR (30 bytes) for +IPv4 address, or \fBDNS_A6RSIZE\fR (74 bytes) for IPv6 address, is sufficient. +.RE + +.PP +.nf +int \fBdns_dntop\fR(\fIdn\fR, \fIname\fR, \fInamesiz\fR) + const unsigned char *\fIdn\fR; + const char *\fIname\fR; unsigned \fInamesiz\fR; +.fi +.RS +convert domain name \fIdn\fR in DN format to asciiz string, placing result +into \fIname\fR buffer of size \fInamesiz\fR. Maximum length of asciiz +representation of domain name is \fBDNS_MAXNAME\fR (1024) bytes. Root +domain is represented as empty string. Return length of the resulting name +(including terminating character, i.e. strlen(name)+1) on success, 0 if the +\fIname\fR buffer is too small, or negative value if \fIdn\fR is invalid +(last case should never happen since all routines in this library which +produce domain names ensure the DNs generated are valid). +.RE + +.PP +.nf +const char *\fBdns_dntosp\fR(const unsigned char *\fIdn\fR) +.fi +.RS +convert domain name \fIdn\fR in DN format to asciiz string using static +buffer. Return the resulting asciiz string on success or NULL on failure. +Note since this routine uses static buffer, it is not thread-safe. +.RE + +.PP +.nf +unsigned \fBdns_dntop_size\fR(const unsigned char *\fIdn\fR) +.fi +.RS +return the buffer size needed to convert the \fIdn\fR domain name +in DN format to asciiz string, for \fBdns_dntop\fR(). The routine +return either the size of buffer required, including the trailing +zero byte, or 0 if \fIdn\fR is invalid. +.RE + +.SS "Working with DNS Packets" + +.PP +The following routines are provided to encode and decode DNS on-wire +packets. This is low-level interface. + +.PP +DNS response codes (returned by \fBdns_rcode\fR() routine) are +defined as constants prefixed with \fBDNS_R_\fR. See udns.h +header file for the complete list. In particular, constants +\fBDNS_R_NOERROR\fR (0), \fBDNS_R_SERVFAIL\fR, \fBDNS_R_NXDOMAIN\fR +may be of interest to an application. + +.PP +.nf +unsigned \fBdns_get16\fR(const unsigned char *\fIp\fR) +unsigned \fBdns_get32\fR(const unsigned char *\fIp\fR) +.fi +.RS +helper routines, convert 16-bit or 32-bit integer in on-wire +format pointed to by \fIp\fR to unsigned. +.RE + +.PP +.nf +unsigned char *\fBdns_put16\fR(unsigned char *\fId\fR, unsigned \fIn\fR) +unsigned char *\fBdns_put32\fR(unsigned char *\fId\fR, unsigned \fIn\fR) +.fi +.RS +helper routine, convert unsigned 16-bit or 32-bit integer \fIn\fR to +on-wire format to buffer pointed to by \fId\fR, return \fId\fR+2 or +\fId\fR+4. +.RE + +.PP +.nf +\fBDNS_HSIZE\fR (12) +.fi +.RS +defines size of DNS header. Data section +in the DNS packet immediately follows the header. In the header, +there are query identifier (id), various flags and codes, +and number of resource records in various data sections. +See udns.h header file for complete list of DNS header definitions. +.RE + +.PP +.nf +unsigned \fBdns_qid\fR(const unsigned char *\fIpkt\fR) +int \fBdns_rd\fR(const unsigned char *\fIpkt\fR) +int \fBdns_tc\fR(const unsigned char *\fIpkt\fR) +int \fBdns_aa\fR(const unsigned char *\fIpkt\fR) +int \fBdns_qr\fR(const unsigned char *\fIpkt\fR) +int \fBdns_ra\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_opcode\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_rcode\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numqd\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numan\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numns\fR(const unsigned char *\fIpkt\fR) +unsigned \fBdns_numar\fR(const unsigned char *\fIpkt\fR) +const unsigned char *\fBdns_payload\fR(const unsigned char *\fIpkt\fR) +.fi +.RS +return various parts from the DNS packet header \fIpkt\fR: +query identifier (qid), +recursion desired (rd) flag, +truncation occured (tc) flag, +authoritative answer (aa) flag, +query response (qr) flag, +recursion available (ra) flag, +operation code (opcode), +result code (rcode), +number of entries in question section (numqd), +number of answers (numan), +number of authority records (numns), +number of additional records (numar), +and the pointer to the packet data (payload). +.RE + +.PP +.nf +int \fBdns_getdn\fR(\fIpkt\fR, \fIcurp\fR, \fIpkte\fR, \fIdn\fR, \fIdnsiz\fR) +const unsigned char *\fBdns_skipdn\fR(\fIcur\fR, \fIpkte\fR) + const unsigned char *\fIpkt\fR, *\fIpkte\fR, **\fIcurp\fR, *\fIcur\fR; + unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR; +.fi +.RS +\fBdns_getdn\fR() extract DN from DNS packet \fIpkt\fR which ends before +\fIpkte\fR starting at position *\fIcurp\fR into buffer pointed to by +\fIdn\fR of size \fIdnsiz\fR. Upon successeful completion, *\fIcurp\fR +will point to the next byte in the packet after the extracted domain name. +It return positive number (length of the DN if \fIdn\fR) upon successeful +completion, negative value on error (when the packet contains invalid data), +or zero if the \fIdnsiz\fR is too small (maximum length of a domain name is +\fBDNS_MAXDN\fR). \fBdns_skipdn\fR() return pointer to the next byte in +DNS packet which ends up before \fIpkte\fR after a domain name which starts +at the \fIcur\fP byte, or NULL if the packet is invalid. \fBdns_skipdn\fR() +is more or less equivalent to what \fBdns_getdn\fR() does, except it does not +actually extract the domain name in question, and uses simpler interface. +.RE + +.PP +.nf +struct \fBdns_rr\fR { + unsigned char \fBdnsrr_dn\fR[DNS_MAXDN]; /* the RR DN name */ + enum dns_class \fBdnsrr_cls\fR; /* class of the RR */ + enum dns_type \fBdnsrr_typ\fR; /* type of the RR */ + unsigned \fBdnsrr_ttl\fR; /* TTL value */ + unsigned \fBdnsrr_dsz\fR; /* size of data in bytes */ + const unsigned char *\fBdnsrr_dptr\fR; /* pointer to the first data byte */ + const unsigned char *\fBdnsrr_dend\fR; /* next byte after RR */ +}; +.fi +.RS +The \fBdns_rr\fR structure is used to hold information about +single DNS Resource Record (RR) in an easy to use form. +.RE + +.PP +.nf +struct \fBdns_parse\fR { + const unsigned char *\fBdnsp_pkt\fR; /* pointer to the packet being parsed */ + const unsigned char *\fBdnsp_end\fR; /* end of the packet pointer */ + const unsigned char *\fBdnsp_cur\fR; /* current packet positionn */ + const unsigned char *\fBdnsp_ans\fR; /* pointer to the answer section */ + int \fBdnsp_rrl\fR; /* number of RRs left */ + int \fBdnsp_nrr\fR; /* number of relevant RRs seen so far */ + unsigned \fBdnsp_ttl\fR; /* TTL value so far */ + const unsigned char *\fBdnsp_qdn\fR; /* the domain of interest or NULL */ + enum dns_class \fBdnsp_qcls\fR; /* class of interest or 0 for any */ + enum dns_type \fBdnsp_qtyp\fR; /* type of interest or 0 for any */ + unsigned char \fBdnsp_dnbuf\fR[DNS_MAXDN]; /* domain name buffer */ +}; +.fi +.RS +The \fBdns_parse\fR structure is used to parse DNS reply packet. +It holds information about the packet being parsed (dnsp_pkt, dnsp_end and +dnsp_cur fields), number of RRs in the current section left to do, and +the information about specific RR which we're looking for (dnsp_qdn, +dnsp_qcls and dnsp_qtyp fields). +.RE + +.PP +.nf +int \fBdns_initparse\fR(struct dns_parse *\fIp\fR, + const unsigned char *\fIqdn\fR, + const unsigned char *\fIpkt\fR, + const unsigned char *\fIcur\fR, + const unsigned char *\fIend\fR) +.fi +.RS +initializes the RR parsing structure \fIp\fR. Arguments \fIpkt\fR, \fIcur\fR +and \fIend\fR should describe the received packet: \fIpkt\fR is the start of +the packet, \fIend\fR points to the next byte after the end of the packet, +and \fIcur\fR points past the query DN in query section (to query class+type +information). And \fIqdn\fR points to the query DN. This is the arguments +passed to \fBdns_parse_fn\fR() routine. \fBdns_initparse\fR() initializes +\fBdnsp_pkt\fR, \fBdnsp_end\fR and \fBdnsp_qdn\fR fields to the corresponding +arguments, extracts and initializes \fBdnsp_qcls\fR and \fBdnsp_qtyp\fR +fields to the values found at \fIcur\fR pointer, initializes +\fBdnsp_cur\fR and \fBdnsp_ans\fR fields to be \fIcur\fR+4 (to the start of +answer section), and initializes \fBdnsp_rrl\fR field to be number of entries +in answer section. \fBdnsp_ttl\fR will be set to max TTL value, 0xffffffff, +and \fBdnsp_nrr\fR to 0. +.RE + +.PP +.nf +int \fBdns_nextrr\fR(struct dns_parse *\fIp\fR, struct dns_rr *\fIrr\fR); +.fi +.RS +searches for next RR in the packet based on the criteria provided in +the \fIp\fR structure, filling in the \fIrr\fR structure and +advancing \fIp\fR->\fBdnsp_cur\fR to the next RR in the packet. +RR selection is based on dnsp_qdn, dnsp_qcls and dnsp_qtyp fields in +the dns_parse structure. Any (or all) of the 3 fields may be 0, +which means any actual value from the packet is acceptable. In case +the field isn't 0 (or NULL for dnsp_qdn), only RRs with corresponding +characteristics are acceptable. Additionally, when dnsp_qdn is non-NULL, +\fBdns_nextrr\fR() performs automatic CNAME expansion. +Routine will return positive value on success, 0 in case it reached the end +of current section in the packet (\fIp\fR->\fBdnsp_rrl\fR is zero), or +negative value if next RR can not be decoded (packet format is invalid). +The routine updates \fIp\fR->\fBdnsp_qdn\fR automatically when this +field is non-NULL and it encounters appropriate CNAME RRs (saving CNAME +target in \fIp\fR->\fBdnsp_dnbuf\fR), so after end of the process, +\fIp\fR->\fBdnsp_qdn\fR will point to canonical name of the domain +in question. The routine updates \fIp\fR->\fBdnsp_ttl\fR value to +be the minimum TTL of all RRs found. +.RE + +.PP +.nf +void \fBdns_rewind\fR(struct dns_parse *\fIp\fR, const unsigned char *\fIqdn\fR) +.fi +.RS +this routine "rewinds" the packet parse state structure to be at the +same state as after a call to \fBdns_initparse\fR(), i.e. reposition +the parse structure \fIp\fR to the start of answer section and +initialize \fIp\fR->\fBdnsp_rrl\fR to the number of entries in +answer section. +.RE + +.PP +.nf +int \fBdns_stdrr_size\fR(const struct dns_parse *\fIp\fR); +.fi +.RS +return size to hold standard RRset structure information, as shown +in \fBdns_rr_null\fR structure (for the query and canonical +names). Used to calculate amount of memory to allocate for common +part of type-specific RR structures in parsing routines. +.RE + +.PP +.nf +void *\fBdns_stdrr_finish\fR(struct dns_rr_null *\fIret\fR, char *\fIcp\fR, + const struct dns_parse *\fIp\fR); +.fi +.RS +initializes standard RRset fields in \fIret\fR structure using buffer +pointed to by \fIcp\fR, which should have at least as many bytes +as \fBdns_stdrr_size\fR(\fIp\fR) returned. Used to finalize common +part of type-specific RR structures in parsing routines. +.RE + +.PP +See library source for usage examples of all the above low-level routines, +especially source of the parsing routines. + +.SS "Auxilary Routines" + +.PP +.nf +int \fBdns_pton\fR(int \fIaf\fR, const char *\fIsrc\fR, void *\fIdst\fR); +.fi +.RS +privides functionality similar to standard \fBinet_pton\fR() routine, +to convert printable representation of an IP address of family \fIaf\fR +(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR into +binary form suitable for socket addresses and transmission over network, +in buffer pointed to by \fIdst\fR. The destination buffer should be +of size 4 for \fBAF_INET\fR family or 16 for \fBAF_INET6\fR. +The return value is positive on success, 0 if \fIsrc\fR is not a valid text +representation of an address of family \fIaf\fR, or negative if the +given address family is not supported. +.RE + +.PP +.nf +const char *\fBdns_ntop\fR(int \fIaf\fR, const void *\fIsrc\fR, + char *\fIdst\fR, int \fIdstsize\fR) +.fi +.RS +privides functionality similar to standard \fBinet_ntop\fR() routine, +to convert binary representation of an IP address of family \fIaf\fR +(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR +(either 4 or 16 bytes) into printable form in buffer in buffer pointed +to by \fIdst\fR of size \fIdstsize\fR. The destination buffer should be +at least of size 16 bytes for \fBAF_INET\fR family or 46 bytes for +\fBAF_INET6\fR. The return value is either \fIdst\fR, or NULL pointer +if \fIdstsize\fR is too small to hold this address or if the given +address family is not supported. +.RE + +.SH AUTHOR +.PP +The \fBudns\fR library has been written by Michael Tokarev, mjt+udns@tls.msk.ru. + +.SH VERSION +.PP +This manual page corresponds to udns version 0.4, released Jan-2014. diff --git a/ami_test_client/libc-jss/udns/udns.h b/ami_test_client/libc-jss/udns/udns.h new file mode 100644 index 0000000000..371e697640 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns.h @@ -0,0 +1,778 @@ +/* udns.h + header file for the UDNS library. + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifndef UDNS_VERSION /* include guard */ + +#define UDNS_VERSION "0.4" + +#ifdef WINDOWS +# ifdef UDNS_DYNAMIC_LIBRARY +# ifdef DNS_LIBRARY_BUILD +# define UDNS_API __declspec(dllexport) +# define UDNS_DATA_API __declspec(dllexport) +# else +# define UDNS_API __declspec(dllimport) +# define UDNS_DATA_API __declspec(dllimport) +# endif +# endif +#endif + +#ifndef UDNS_API +# define UDNS_API +#endif +#ifndef UDNS_DATA_API +# define UDNS_DATA_API +#endif + +#include /* for time_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* forward declarations if sockets stuff isn't #include'd */ +struct in_addr; +struct in6_addr; +struct sockaddr; + +/**************************************************************************/ +/**************** Common definitions **************************************/ + +UDNS_API const char * +dns_version(void); + +struct dns_ctx; +struct dns_query; + +/* shorthand for [const] unsigned char */ +typedef unsigned char dnsc_t; +typedef const unsigned char dnscc_t; + +#define DNS_MAXDN 255 /* max DN length */ +#define DNS_DNPAD 1 /* padding for DN buffers */ +#define DNS_MAXLABEL 63 /* max DN label length */ +#define DNS_MAXNAME 1024 /* max asciiz domain name length */ +#define DNS_HSIZE 12 /* DNS packet header size */ +#define DNS_PORT 53 /* default domain port */ +#define DNS_MAXSERV 6 /* max servers to consult */ +#define DNS_MAXPACKET 512 /* max traditional-DNS UDP packet size */ +#define DNS_EDNS0PACKET 4096 /* EDNS0 packet size to use */ + +enum dns_class { /* DNS RR Classes */ + DNS_C_INVALID = 0, /* invalid class */ + DNS_C_IN = 1, /* Internet */ + DNS_C_CH = 3, /* CHAOS */ + DNS_C_HS = 4, /* HESIOD */ + DNS_C_ANY = 255 /* wildcard */ +}; + +enum dns_type { /* DNS RR Types */ + DNS_T_INVALID = 0, /* Cookie. */ + DNS_T_A = 1, /* Host address. */ + DNS_T_NS = 2, /* Authoritative server. */ + DNS_T_MD = 3, /* Mail destination. */ + DNS_T_MF = 4, /* Mail forwarder. */ + DNS_T_CNAME = 5, /* Canonical name. */ + DNS_T_SOA = 6, /* Start of authority zone. */ + DNS_T_MB = 7, /* Mailbox domain name. */ + DNS_T_MG = 8, /* Mail group member. */ + DNS_T_MR = 9, /* Mail rename name. */ + DNS_T_NULL = 10, /* Null resource record. */ + DNS_T_WKS = 11, /* Well known service. */ + DNS_T_PTR = 12, /* Domain name pointer. */ + DNS_T_HINFO = 13, /* Host information. */ + DNS_T_MINFO = 14, /* Mailbox information. */ + DNS_T_MX = 15, /* Mail routing information. */ + DNS_T_TXT = 16, /* Text strings. */ + DNS_T_RP = 17, /* Responsible person. */ + DNS_T_AFSDB = 18, /* AFS cell database. */ + DNS_T_X25 = 19, /* X_25 calling address. */ + DNS_T_ISDN = 20, /* ISDN calling address. */ + DNS_T_RT = 21, /* Router. */ + DNS_T_NSAP = 22, /* NSAP address. */ + DNS_T_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + DNS_T_SIG = 24, /* Security signature. */ + DNS_T_KEY = 25, /* Security key. */ + DNS_T_PX = 26, /* X.400 mail mapping. */ + DNS_T_GPOS = 27, /* Geographical position (withdrawn). */ + DNS_T_AAAA = 28, /* Ip6 Address. */ + DNS_T_LOC = 29, /* Location Information. */ + DNS_T_NXT = 30, /* Next domain (security). */ + DNS_T_EID = 31, /* Endpoint identifier. */ + DNS_T_NIMLOC = 32, /* Nimrod Locator. */ + DNS_T_SRV = 33, /* Server Selection. */ + DNS_T_ATMA = 34, /* ATM Address */ + DNS_T_NAPTR = 35, /* Naming Authority PoinTeR */ + DNS_T_KX = 36, /* Key Exchange */ + DNS_T_CERT = 37, /* Certification record */ + DNS_T_A6 = 38, /* IPv6 address (deprecates AAAA) */ + DNS_T_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + DNS_T_SINK = 40, /* Kitchen sink (experimentatl) */ + DNS_T_OPT = 41, /* EDNS0 option (meta-RR) */ + DNS_T_DS = 43, /* DNSSEC */ + DNS_T_SSHFP = 44, + DNS_T_IPSECKEY = 45, + DNS_T_RRSIG = 46, /* DNSSEC */ + DNS_T_NSEC = 47, /* DNSSEC */ + DNS_T_DNSKEY = 48, + DNS_T_DHCID = 49, + DNS_T_NSEC3 = 50, + DNS_T_NSEC3PARAMS = 51, + DNS_T_TALINK = 58, /* draft-ietf-dnsop-trust-history */ + DNS_T_SPF = 99, + DNS_T_UINFO = 100, + DNS_T_UID = 101, + DNS_T_GID = 102, + DNS_T_UNSPEC = 103, + DNS_T_TSIG = 250, /* Transaction signature. */ + DNS_T_IXFR = 251, /* Incremental zone transfer. */ + DNS_T_AXFR = 252, /* Transfer zone of authority. */ + DNS_T_MAILB = 253, /* Transfer mailbox records. */ + DNS_T_MAILA = 254, /* Transfer mail agent records. */ + DNS_T_ANY = 255, /* Wildcard match. */ + DNS_T_ZXFR = 256, /* BIND-specific, nonstandard. */ + DNS_T_DLV = 32769, /* RFC 4431, 5074, DNSSEC Lookaside Validation */ + DNS_T_MAX = 65536 +}; + +/**************************************************************************/ +/**************** Domain Names (DNs) **************************************/ + +/* return length of the DN */ +UDNS_API unsigned +dns_dnlen(dnscc_t *dn); + +/* return #of labels in a DN */ +UDNS_API unsigned +dns_dnlabels(dnscc_t *dn); + +/* lower- and uppercase single DN char */ +#define DNS_DNLC(c) ((c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 'a' : (c)) +#define DNS_DNUC(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) + +/* compare the DNs, return dnlen of equal or 0 if not */ +UDNS_API unsigned +dns_dnequal(dnscc_t *dn1, dnscc_t *dn2); + +/* copy one DN to another, size checking */ +UDNS_API unsigned +dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz); + +/* convert asciiz string of length namelen (0 to use strlen) to DN */ +UDNS_API int +dns_ptodn(const char *name, unsigned namelen, + dnsc_t *dn, unsigned dnsiz, int *isabs); + +/* simpler form of dns_ptodn() */ +#define dns_sptodn(name,dn,dnsiz) dns_ptodn((name),0,(dn),(dnsiz),0) + +UDNS_DATA_API extern dnscc_t dns_inaddr_arpa_dn[14]; +#define DNS_A4RSIZE 30 +UDNS_API int +dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz); +UDNS_API int +dns_a4ptodn(const struct in_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz); +UDNS_API dnsc_t * +dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne); + +UDNS_DATA_API extern dnscc_t dns_ip6_arpa_dn[10]; +#define DNS_A6RSIZE 74 +UDNS_API int +dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz); +UDNS_API int +dns_a6ptodn(const struct in6_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz); +UDNS_API dnsc_t * +dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne); + +/* convert DN into asciiz string */ +UDNS_API int +dns_dntop(dnscc_t *dn, char *name, unsigned namesiz); + +/* convert DN into asciiz string, using static buffer (NOT thread-safe!) */ +UDNS_API const char * +dns_dntosp(dnscc_t *dn); + +/* return buffer size (incl. null byte) required for asciiz form of a DN */ +UDNS_API unsigned +dns_dntop_size(dnscc_t *dn); + +/* either wrappers or reimplementations for inet_ntop() and inet_pton() */ +UDNS_API const char *dns_ntop(int af, const void *src, char *dst, int size); +UDNS_API int dns_pton(int af, const char *src, void *dst); + +/**************************************************************************/ +/**************** DNS raw packet layout ***********************************/ + +enum dns_rcode { /* reply codes */ + DNS_R_NOERROR = 0, /* ok, no error */ + DNS_R_FORMERR = 1, /* format error */ + DNS_R_SERVFAIL = 2, /* server failed */ + DNS_R_NXDOMAIN = 3, /* domain does not exists */ + DNS_R_NOTIMPL = 4, /* not implemented */ + DNS_R_REFUSED = 5, /* query refused */ + /* these are for BIND_UPDATE */ + DNS_R_YXDOMAIN = 6, /* Name exists */ + DNS_R_YXRRSET = 7, /* RRset exists */ + DNS_R_NXRRSET = 8, /* RRset does not exist */ + DNS_R_NOTAUTH = 9, /* Not authoritative for zone */ + DNS_R_NOTZONE = 10, /* Zone of record different from zone section */ + /*ns_r_max = 11,*/ + /* The following are TSIG extended errors */ + DNS_R_BADSIG = 16, + DNS_R_BADKEY = 17, + DNS_R_BADTIME = 18 +}; + +static __inline unsigned dns_get16(dnscc_t *s) { + return ((unsigned)s[0]<<8) | s[1]; +} +static __inline unsigned dns_get32(dnscc_t *s) { + return ((unsigned)s[0]<<24) | ((unsigned)s[1]<<16) + | ((unsigned)s[2]<<8) | s[3]; +} +static __inline dnsc_t *dns_put16(dnsc_t *d, unsigned n) { + *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); return d; +} +static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) { + *d++ = (dnsc_t)((n >> 24) & 255); *d++ = (dnsc_t)((n >> 16) & 255); + *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); + return d; +} + +/* DNS Header layout */ +enum { + /* bytes 0:1 - query ID */ + DNS_H_QID1 = 0, + DNS_H_QID2 = 1, + DNS_H_QID = DNS_H_QID1, +#define dns_qid(pkt) dns_get16((pkt)+DNS_H_QID) + /* byte 2: flags1 */ + DNS_H_F1 = 2, + DNS_HF1_QR = 0x80, /* query response flag */ +#define dns_qr(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_QR) + DNS_HF1_OPCODE = 0x78, /* opcode, 0 = query */ +#define dns_opcode(pkt) (((pkt)[DNS_H_F1]&DNS_HF1_OPCODE)>>3) + DNS_HF1_AA = 0x04, /* auth answer */ +#define dns_aa(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_AA) + DNS_HF1_TC = 0x02, /* truncation flag */ +#define dns_tc(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_TC) + DNS_HF1_RD = 0x01, /* recursion desired (may be set in query) */ +#define dns_rd(pkt) ((pkt)[DNS_H_F1]&DNS_HF1_RD) + /* byte 3: flags2 */ + DNS_H_F2 = 3, + DNS_HF2_RA = 0x80, /* recursion available */ +#define dns_ra(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RA) + DNS_HF2_Z = 0x40, /* reserved */ + DNS_HF2_AD = 0x20, /* DNSSEC: authentic data */ +#define dns_ad(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_AD) + DNS_HF2_CD = 0x10, /* DNSSEC: checking disabled */ +#define dns_cd(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_CD) + DNS_HF2_RCODE = 0x0f, /* response code, DNS_R_XXX above */ +#define dns_rcode(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RCODE) + /* bytes 4:5: qdcount, numqueries */ + DNS_H_QDCNT1 = 4, + DNS_H_QDCNT2 = 5, + DNS_H_QDCNT = DNS_H_QDCNT1, +#define dns_numqd(pkt) dns_get16((pkt)+4) + /* bytes 6:7: ancount, numanswers */ + DNS_H_ANCNT1 = 6, + DNS_H_ANCNT2 = 7, + DNS_H_ANCNT = DNS_H_ANCNT1, +#define dns_numan(pkt) dns_get16((pkt)+6) + /* bytes 8:9: nscount, numauthority */ + DNS_H_NSCNT1 = 8, + DNS_H_NSCNT2 = 9, + DNS_H_NSCNT = DNS_H_NSCNT1, +#define dns_numns(pkt) dns_get16((pkt)+8) + /* bytes 10:11: arcount, numadditional */ + DNS_H_ARCNT1 = 10, + DNS_H_ARCNT2 = 11, + DNS_H_ARCNT = DNS_H_ARCNT1, +#define dns_numar(pkt) dns_get16((pkt)+10) +#define dns_payload(pkt) ((pkt)+DNS_HSIZE) + /* EDNS0 (OPT RR) flags (Ext. Flags) */ + DNS_EF1_DO = 0x80, /* DNSSEC OK */ +}; + +/* packet buffer: start at pkt, end before pkte, current pos *curp. + * extract a DN and set *curp to the next byte after DN in packet. + * return -1 on error, 0 if dnsiz is too small, or dnlen on ok. + */ +UDNS_API int +dns_getdn(dnscc_t *pkt, dnscc_t **curp, dnscc_t *end, + dnsc_t *dn, unsigned dnsiz); + +/* skip the DN at position cur in packet ending before pkte, + * return pointer to the next byte after the DN or NULL on error */ +UDNS_API dnscc_t * +dns_skipdn(dnscc_t *end, dnscc_t *cur); + +struct dns_rr { /* DNS Resource Record */ + dnsc_t dnsrr_dn[DNS_MAXDN]; /* the DN of the RR */ + enum dns_class dnsrr_cls; /* Class */ + enum dns_type dnsrr_typ; /* Type */ + unsigned dnsrr_ttl; /* Time-To-Live (TTL) */ + unsigned dnsrr_dsz; /* data size */ + dnscc_t *dnsrr_dptr; /* pointer to start of data */ + dnscc_t *dnsrr_dend; /* past end of data */ +}; + +struct dns_parse { /* RR/packet parsing state */ + dnscc_t *dnsp_pkt; /* start of the packet */ + dnscc_t *dnsp_end; /* end of the packet */ + dnscc_t *dnsp_cur; /* current packet position */ + dnscc_t *dnsp_ans; /* start of answer section */ + int dnsp_rrl; /* number of RRs left to go */ + int dnsp_nrr; /* RR count so far */ + unsigned dnsp_ttl; /* TTL value so far */ + dnscc_t *dnsp_qdn; /* the RR DN we're looking for */ + enum dns_class dnsp_qcls; /* RR class we're looking for or 0 */ + enum dns_type dnsp_qtyp; /* RR type we're looking for or 0 */ + dnsc_t dnsp_dnbuf[DNS_MAXDN]; /* domain buffer */ +}; + +/* initialize the parse structure */ +UDNS_API void +dns_initparse(struct dns_parse *p, dnscc_t *qdn, + dnscc_t *pkt, dnscc_t *cur, dnscc_t *end); + +/* search next RR, <0=error, 0=no more RRs, >0 = found. */ +UDNS_API int +dns_nextrr(struct dns_parse *p, struct dns_rr *rr); + +UDNS_API void +dns_rewind(struct dns_parse *p, dnscc_t *qdn); + + +/**************************************************************************/ +/**************** Resolver Context ****************************************/ + +/* default resolver context */ +UDNS_DATA_API extern struct dns_ctx dns_defctx; + +/* reset resolver context to default state, close it if open, drop queries */ +UDNS_API void +dns_reset(struct dns_ctx *ctx); + +/* reset resolver context and read in system configuration */ +UDNS_API int +dns_init(struct dns_ctx *ctx, int do_open); + +/* return new resolver context with the same settings as copy */ +UDNS_API struct dns_ctx * +dns_new(const struct dns_ctx *copy); + +/* free resolver context returned by dns_new(); all queries are dropped */ +UDNS_API void +dns_free(struct dns_ctx *ctx); + +/* add nameserver for a resolver context (or reset nslist if serv==NULL) */ +UDNS_API int +dns_add_serv(struct dns_ctx *ctx, const char *serv); + +/* add nameserver using struct sockaddr structure (with ports) */ +UDNS_API int +dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa); + +/* add search list element for a resolver context (or reset it if srch==NULL) */ +UDNS_API int +dns_add_srch(struct dns_ctx *ctx, const char *srch); + +/* set options for a resolver context */ +UDNS_API int +dns_set_opts(struct dns_ctx *ctx, const char *opts); + +enum dns_opt { /* options */ + DNS_OPT_FLAGS, /* flags, DNS_F_XXX */ + DNS_OPT_TIMEOUT, /* timeout in secounds */ + DNS_OPT_NTRIES, /* number of retries */ + DNS_OPT_NDOTS, /* ndots */ + DNS_OPT_UDPSIZE, /* EDNS0 UDP size */ + DNS_OPT_PORT, /* port to use */ +}; + +/* set or get (if val<0) an option */ +UDNS_API int +dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val); + +enum dns_flags { + DNS_NOSRCH = 0x00010000, /* do not perform search */ + DNS_NORD = 0x00020000, /* request no recursion */ + DNS_AAONLY = 0x00040000, /* set AA flag in queries */ + DNS_SET_DO = 0x00080000, /* set EDNS0 "DO" bit (DNSSEC OK) */ + DNS_SET_CD = 0x00100000, /* set CD bit (DNSSEC: checking disabled) */ +}; + +/* set the debug function pointer */ +typedef void +(dns_dbgfn)(int code, const struct sockaddr *sa, unsigned salen, + dnscc_t *pkt, int plen, + const struct dns_query *q, void *data); +UDNS_API void +dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn); + +/* open and return UDP socket */ +UDNS_API int +dns_open(struct dns_ctx *ctx); + +/* return UDP socket or -1 if not open */ +UDNS_API int +dns_sock(const struct dns_ctx *ctx); + +/* close the UDP socket */ +UDNS_API void +dns_close(struct dns_ctx *ctx); + +/* return number of requests queued */ +UDNS_API int +dns_active(const struct dns_ctx *ctx); + +/* return status of the last operation */ +UDNS_API int +dns_status(const struct dns_ctx *ctx); +UDNS_API void +dns_setstatus(struct dns_ctx *ctx, int status); + +/* handle I/O event on UDP socket */ +UDNS_API void +dns_ioevent(struct dns_ctx *ctx, time_t now); + +/* process any timeouts, return time in secounds to the + * next timeout (or -1 if none) but not greather than maxwait */ +UDNS_API int +dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now); + +/* define timer requesting routine to use */ +typedef void dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data); +UDNS_API void +dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data); + +/**************************************************************************/ +/**************** Making Queries ******************************************/ + +/* query callback routine */ +typedef void dns_query_fn(struct dns_ctx *ctx, void *result, void *data); + +/* query parse routine: raw DNS => application structure */ +typedef int +dns_parse_fn(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **res); + +enum dns_status { + DNS_E_NOERROR = 0, /* ok, not an error */ + DNS_E_TEMPFAIL = -1, /* timeout, SERVFAIL or similar */ + DNS_E_PROTOCOL = -2, /* got garbled reply */ + DNS_E_NXDOMAIN = -3, /* domain does not exists */ + DNS_E_NODATA = -4, /* domain exists but no data of reqd type */ + DNS_E_NOMEM = -5, /* out of memory while processing */ + DNS_E_BADQUERY = -6 /* the query is malformed */ +}; + +/* submit generic DN query */ +UDNS_API struct dns_query * +dns_submit_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data); +/* submit generic name query */ +UDNS_API struct dns_query * +dns_submit_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data); + +/* cancel the given async query in progress */ +UDNS_API int +dns_cancel(struct dns_ctx *ctx, struct dns_query *q); + +/* resolve a generic query, return the answer */ +UDNS_API void * +dns_resolve_dn(struct dns_ctx *ctx, + dnscc_t *qdn, int qcls, int qtyp, int flags, + dns_parse_fn *parse); +UDNS_API void * +dns_resolve_p(struct dns_ctx *ctx, + const char *qname, int qcls, int qtyp, int flags, + dns_parse_fn *parse); +UDNS_API void * +dns_resolve(struct dns_ctx *ctx, struct dns_query *q); + + +/* Specific RR handlers */ + +#define dns_rr_common(prefix) \ + char *prefix##_cname; /* canonical name */ \ + char *prefix##_qname; /* original query name */ \ + unsigned prefix##_ttl; /* TTL value */ \ + int prefix##_nrr /* number of records */ + +struct dns_rr_null { /* NULL RRset, aka RRset template */ + dns_rr_common(dnsn); +}; + +UDNS_API int +dns_stdrr_size(const struct dns_parse *p); +UDNS_API void * +dns_stdrr_finish(struct dns_rr_null *ret, char *cp, const struct dns_parse *p); + +struct dns_rr_a4 { /* the A RRset */ + dns_rr_common(dnsa4); + struct in_addr *dnsa4_addr; /* array of addresses, naddr elements */ +}; + +UDNS_API dns_parse_fn dns_parse_a4; /* A RR parsing routine */ +typedef void /* A query callback routine */ +dns_query_a4_fn(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data); + +/* submit A IN query */ +UDNS_API struct dns_query * +dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a4_fn *cbck, void *data); + +/* resolve A IN query */ +UDNS_API struct dns_rr_a4 * +dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_rr_a6 { /* the AAAA RRset */ + dns_rr_common(dnsa6); + struct in6_addr *dnsa6_addr; /* array of addresses, naddr elements */ +}; + +UDNS_API dns_parse_fn dns_parse_a6; /* A RR parsing routine */ +typedef void /* A query callback routine */ +dns_query_a6_fn(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data); + +/* submit AAAA IN query */ +UDNS_API struct dns_query * +dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a6_fn *cbck, void *data); + +/* resolve AAAA IN query */ +UDNS_API struct dns_rr_a6 * +dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_rr_ptr { /* the PTR RRset */ + dns_rr_common(dnsptr); + char **dnsptr_ptr; /* array of PTRs */ +}; + +UDNS_API dns_parse_fn dns_parse_ptr; /* PTR RR parsing routine */ +typedef void /* PTR query callback */ +dns_query_ptr_fn(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data); +/* submit PTR IN in-addr.arpa query */ +UDNS_API struct dns_query * +dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, + dns_query_ptr_fn *cbck, void *data); +/* resolve PTR IN in-addr.arpa query */ +UDNS_API struct dns_rr_ptr * +dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr); + +/* the same as above, but for ip6.arpa */ +UDNS_API struct dns_query * +dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, + dns_query_ptr_fn *cbck, void *data); +UDNS_API struct dns_rr_ptr * +dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr); + + +struct dns_mx { /* single MX RR */ + int priority; /* MX priority */ + char *name; /* MX name */ +}; +struct dns_rr_mx { /* the MX RRset */ + dns_rr_common(dnsmx); + struct dns_mx *dnsmx_mx; /* array of MXes */ +}; +UDNS_API dns_parse_fn dns_parse_mx; /* MX RR parsing routine */ +typedef void /* MX RR callback */ +dns_query_mx_fn(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data); +/* submit MX IN query */ +UDNS_API struct dns_query * +dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, + dns_query_mx_fn *cbck, void *data); +/* resolve MX IN query */ +UDNS_API struct dns_rr_mx * +dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags); + + +struct dns_txt { /* single TXT record */ + int len; /* length of the text */ + dnsc_t *txt; /* pointer to text buffer. May contain nulls. */ +}; +struct dns_rr_txt { /* the TXT RRset */ + dns_rr_common(dnstxt); + struct dns_txt *dnstxt_txt; /* array of TXT records */ +}; +UDNS_API dns_parse_fn dns_parse_txt; /* TXT RR parsing routine */ +typedef void /* TXT RR callback */ +dns_query_txt_fn(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data); +/* submit TXT query */ +UDNS_API struct dns_query * +dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, + dns_query_txt_fn *cbck, void *data); +/* resolve TXT query */ +UDNS_API struct dns_rr_txt * +dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags); + + +struct dns_srv { /* single SRV RR */ + int priority; /* SRV priority */ + int weight; /* SRV weight */ + int port; /* SRV port */ + char *name; /* SRV name */ +}; +struct dns_rr_srv { /* the SRV RRset */ + dns_rr_common(dnssrv); + struct dns_srv *dnssrv_srv; /* array of SRVes */ +}; +UDNS_API dns_parse_fn dns_parse_srv; /* SRV RR parsing routine */ +typedef void /* SRV RR callback */ +dns_query_srv_fn(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data); +/* submit SRV IN query */ +UDNS_API struct dns_query * +dns_submit_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags, dns_query_srv_fn *cbck, void *data); +/* resolve SRV IN query */ +UDNS_API struct dns_rr_srv * +dns_resolve_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags); + +/* NAPTR (RFC3403) RR type */ +struct dns_naptr { /* single NAPTR RR */ + int order; /* NAPTR order */ + int preference; /* NAPTR preference */ + char *flags; /* NAPTR flags */ + char *service; /* NAPTR service */ + char *regexp; /* NAPTR regexp */ + char *replacement; /* NAPTR replacement */ +}; + +struct dns_rr_naptr { /* the NAPTR RRset */ + dns_rr_common(dnsnaptr); + struct dns_naptr *dnsnaptr_naptr; /* array of NAPTRes */ +}; +UDNS_API dns_parse_fn dns_parse_naptr; /* NAPTR RR parsing routine */ +typedef void /* NAPTR RR callback */ +dns_query_naptr_fn(struct dns_ctx *ctx, + struct dns_rr_naptr *result, void *data); +/* submit NAPTR IN query */ +UDNS_API struct dns_query * +dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, + dns_query_naptr_fn *cbck, void *data); +/* resolve NAPTR IN query */ +UDNS_API struct dns_rr_naptr * +dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags); + + +UDNS_API struct dns_query * +dns_submit_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl); + +UDNS_API struct dns_query * +dns_submit_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl); + +UDNS_API struct dns_query * +dns_submit_rhsbl(struct dns_ctx *ctx, + const char *name, const char *rhsbl, + dns_query_a4_fn *cbck, void *data); +UDNS_API struct dns_query * +dns_submit_rhsbl_txt(struct dns_ctx *ctx, + const char *name, const char *rhsbl, + dns_query_txt_fn *cbck, void *data); +UDNS_API struct dns_rr_a4 * +dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl); +UDNS_API struct dns_rr_txt * +dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl); + +/**************************************************************************/ +/**************** Names, Names ********************************************/ + +struct dns_nameval { + int val; + const char *name; +}; + +UDNS_DATA_API extern const struct dns_nameval dns_classtab[]; +UDNS_DATA_API extern const struct dns_nameval dns_typetab[]; +UDNS_DATA_API extern const struct dns_nameval dns_rcodetab[]; +UDNS_API int +dns_findname(const struct dns_nameval *nv, const char *name); +#define dns_findclassname(cls) dns_findname(dns_classtab, (cls)) +#define dns_findtypename(type) dns_findname(dns_typetab, (type)) +#define dns_findrcodename(rcode) dns_findname(dns_rcodetab, (rcode)) + +UDNS_API const char *dns_classname(enum dns_class cls); +UDNS_API const char *dns_typename(enum dns_type type); +UDNS_API const char *dns_rcodename(enum dns_rcode rcode); +const char *_dns_format_code(char *buf, const char *prefix, int code); + +UDNS_API const char *dns_strerror(int errnum); + +/* simple pseudo-random number generator, code by Bob Jenkins */ + +struct udns_jranctx { /* the context */ + unsigned a, b, c, d; +}; + +/* initialize the RNG with a given seed */ +UDNS_API void +udns_jraninit(struct udns_jranctx *x, unsigned seed); + +/* return next random number. 32bits on most platforms so far. */ +UDNS_API unsigned +udns_jranval(struct udns_jranctx *x); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* include guard */ diff --git a/ami_test_client/libc-jss/udns/udns_XtoX.c b/ami_test_client/libc-jss/udns/udns_XtoX.c new file mode 100644 index 0000000000..60e3fdfa32 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_XtoX.c @@ -0,0 +1,50 @@ +/* udns_XtoX.c + udns_ntop() and udns_pton() routines, which are either + - wrappers for inet_ntop() and inet_pton() or + - reimplementations of those routines. + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "udns.h" + +#ifdef HAVE_INET_PTON_NTOP + +#include +#include +#include + +const char *dns_ntop(int af, const void *src, char *dst, int size) { + return inet_ntop(af, src, dst, size); +} + +int dns_pton(int af, const char *src, void *dst) { + return inet_pton(af, src, dst); +} + +#else + +#define inet_XtoX_prefix udns_ +#include "inet_XtoX.c" + +#endif diff --git a/ami_test_client/libc-jss/udns/udns_bl.c b/ami_test_client/libc-jss/udns/udns_bl.c new file mode 100644 index 0000000000..f6be39335f --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_bl.c @@ -0,0 +1,160 @@ +/* udns_bl.c + DNSBL stuff + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" +#ifndef NULL +# define NULL 0 +#endif + +struct dns_query * +dns_submit_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_query * +dns_submit_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a4dnsbl(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl) { + return (struct dns_rr_a4 *) + dns_resolve(ctx, dns_submit_a4dnsbl(ctx, addr, dnsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx, + const struct in_addr *addr, const char *dnsbl) { + return (struct dns_rr_txt *) + dns_resolve(ctx, dns_submit_a4dnsbl_txt(ctx, addr, dnsbl, 0, 0)); +} + + +struct dns_query * +dns_submit_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_query * +dns_submit_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a6dnsbl(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl) { + return (struct dns_rr_a4 *) + dns_resolve(ctx, dns_submit_a6dnsbl(ctx, addr, dnsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx, + const struct in6_addr *addr, const char *dnsbl) { + return (struct dns_rr_txt *) + dns_resolve(ctx, dns_submit_a6dnsbl_txt(ctx, addr, dnsbl, 0, 0)); +} + +static int +dns_rhsbltodn(const char *name, const char *rhsbl, dnsc_t dn[DNS_MAXDN]) +{ + int l = dns_sptodn(name, dn, DNS_MAXDN); + if (l <= 0) return 0; + l = dns_sptodn(rhsbl, dn+l-1, DNS_MAXDN-l+1); + if (l <= 0) return 0; + return 1; +} + +struct dns_query * +dns_submit_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl, + dns_query_a4_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (!dns_rhsbltodn(name, rhsbl, dn)) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH, + dns_parse_a4, (dns_query_fn*)cbck, data); +} +struct dns_query * +dns_submit_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl, + dns_query_txt_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + if (!dns_rhsbltodn(name, rhsbl, dn)) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH, + dns_parse_txt, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl) { + return (struct dns_rr_a4*) + dns_resolve(ctx, dns_submit_rhsbl(ctx, name, rhsbl, 0, 0)); +} + +struct dns_rr_txt * +dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl) +{ + return (struct dns_rr_txt*) + dns_resolve(ctx, dns_submit_rhsbl_txt(ctx, name, rhsbl, 0, 0)); +} diff --git a/ami_test_client/libc-jss/udns/udns_codes.c b/ami_test_client/libc-jss/udns/udns_codes.c new file mode 100644 index 0000000000..c637e98c58 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_codes.c @@ -0,0 +1,199 @@ +/* Automatically generated. */ +#include "udns.h" + +const struct dns_nameval dns_typetab[] = { + {DNS_T_INVALID,"INVALID"}, + {DNS_T_A,"A"}, + {DNS_T_NS,"NS"}, + {DNS_T_MD,"MD"}, + {DNS_T_MF,"MF"}, + {DNS_T_CNAME,"CNAME"}, + {DNS_T_SOA,"SOA"}, + {DNS_T_MB,"MB"}, + {DNS_T_MG,"MG"}, + {DNS_T_MR,"MR"}, + {DNS_T_NULL,"NULL"}, + {DNS_T_WKS,"WKS"}, + {DNS_T_PTR,"PTR"}, + {DNS_T_HINFO,"HINFO"}, + {DNS_T_MINFO,"MINFO"}, + {DNS_T_MX,"MX"}, + {DNS_T_TXT,"TXT"}, + {DNS_T_RP,"RP"}, + {DNS_T_AFSDB,"AFSDB"}, + {DNS_T_X25,"X25"}, + {DNS_T_ISDN,"ISDN"}, + {DNS_T_RT,"RT"}, + {DNS_T_NSAP,"NSAP"}, + {DNS_T_NSAP_PTR,"NSAP_PTR"}, + {DNS_T_SIG,"SIG"}, + {DNS_T_KEY,"KEY"}, + {DNS_T_PX,"PX"}, + {DNS_T_GPOS,"GPOS"}, + {DNS_T_AAAA,"AAAA"}, + {DNS_T_LOC,"LOC"}, + {DNS_T_NXT,"NXT"}, + {DNS_T_EID,"EID"}, + {DNS_T_NIMLOC,"NIMLOC"}, + {DNS_T_SRV,"SRV"}, + {DNS_T_ATMA,"ATMA"}, + {DNS_T_NAPTR,"NAPTR"}, + {DNS_T_KX,"KX"}, + {DNS_T_CERT,"CERT"}, + {DNS_T_A6,"A6"}, + {DNS_T_DNAME,"DNAME"}, + {DNS_T_SINK,"SINK"}, + {DNS_T_OPT,"OPT"}, + {DNS_T_DS,"DS"}, + {DNS_T_SSHFP,"SSHFP"}, + {DNS_T_IPSECKEY,"IPSECKEY"}, + {DNS_T_RRSIG,"RRSIG"}, + {DNS_T_NSEC,"NSEC"}, + {DNS_T_DNSKEY,"DNSKEY"}, + {DNS_T_DHCID,"DHCID"}, + {DNS_T_NSEC3,"NSEC3"}, + {DNS_T_NSEC3PARAMS,"NSEC3PARAMS"}, + {DNS_T_TALINK,"TALINK"}, + {DNS_T_SPF,"SPF"}, + {DNS_T_UINFO,"UINFO"}, + {DNS_T_UID,"UID"}, + {DNS_T_GID,"GID"}, + {DNS_T_UNSPEC,"UNSPEC"}, + {DNS_T_TSIG,"TSIG"}, + {DNS_T_IXFR,"IXFR"}, + {DNS_T_AXFR,"AXFR"}, + {DNS_T_MAILB,"MAILB"}, + {DNS_T_MAILA,"MAILA"}, + {DNS_T_ANY,"ANY"}, + {DNS_T_ZXFR,"ZXFR"}, + {DNS_T_DLV,"DLV"}, + {DNS_T_MAX,"MAX"}, + {0,0}}; +const char *dns_typename(enum dns_type code) { + static char nm[20]; + switch(code) { + case DNS_T_INVALID: return dns_typetab[0].name; + case DNS_T_A: return dns_typetab[1].name; + case DNS_T_NS: return dns_typetab[2].name; + case DNS_T_MD: return dns_typetab[3].name; + case DNS_T_MF: return dns_typetab[4].name; + case DNS_T_CNAME: return dns_typetab[5].name; + case DNS_T_SOA: return dns_typetab[6].name; + case DNS_T_MB: return dns_typetab[7].name; + case DNS_T_MG: return dns_typetab[8].name; + case DNS_T_MR: return dns_typetab[9].name; + case DNS_T_NULL: return dns_typetab[10].name; + case DNS_T_WKS: return dns_typetab[11].name; + case DNS_T_PTR: return dns_typetab[12].name; + case DNS_T_HINFO: return dns_typetab[13].name; + case DNS_T_MINFO: return dns_typetab[14].name; + case DNS_T_MX: return dns_typetab[15].name; + case DNS_T_TXT: return dns_typetab[16].name; + case DNS_T_RP: return dns_typetab[17].name; + case DNS_T_AFSDB: return dns_typetab[18].name; + case DNS_T_X25: return dns_typetab[19].name; + case DNS_T_ISDN: return dns_typetab[20].name; + case DNS_T_RT: return dns_typetab[21].name; + case DNS_T_NSAP: return dns_typetab[22].name; + case DNS_T_NSAP_PTR: return dns_typetab[23].name; + case DNS_T_SIG: return dns_typetab[24].name; + case DNS_T_KEY: return dns_typetab[25].name; + case DNS_T_PX: return dns_typetab[26].name; + case DNS_T_GPOS: return dns_typetab[27].name; + case DNS_T_AAAA: return dns_typetab[28].name; + case DNS_T_LOC: return dns_typetab[29].name; + case DNS_T_NXT: return dns_typetab[30].name; + case DNS_T_EID: return dns_typetab[31].name; + case DNS_T_NIMLOC: return dns_typetab[32].name; + case DNS_T_SRV: return dns_typetab[33].name; + case DNS_T_ATMA: return dns_typetab[34].name; + case DNS_T_NAPTR: return dns_typetab[35].name; + case DNS_T_KX: return dns_typetab[36].name; + case DNS_T_CERT: return dns_typetab[37].name; + case DNS_T_A6: return dns_typetab[38].name; + case DNS_T_DNAME: return dns_typetab[39].name; + case DNS_T_SINK: return dns_typetab[40].name; + case DNS_T_OPT: return dns_typetab[41].name; + case DNS_T_DS: return dns_typetab[42].name; + case DNS_T_SSHFP: return dns_typetab[43].name; + case DNS_T_IPSECKEY: return dns_typetab[44].name; + case DNS_T_RRSIG: return dns_typetab[45].name; + case DNS_T_NSEC: return dns_typetab[46].name; + case DNS_T_DNSKEY: return dns_typetab[47].name; + case DNS_T_DHCID: return dns_typetab[48].name; + case DNS_T_NSEC3: return dns_typetab[49].name; + case DNS_T_NSEC3PARAMS: return dns_typetab[50].name; + case DNS_T_TALINK: return dns_typetab[51].name; + case DNS_T_SPF: return dns_typetab[52].name; + case DNS_T_UINFO: return dns_typetab[53].name; + case DNS_T_UID: return dns_typetab[54].name; + case DNS_T_GID: return dns_typetab[55].name; + case DNS_T_UNSPEC: return dns_typetab[56].name; + case DNS_T_TSIG: return dns_typetab[57].name; + case DNS_T_IXFR: return dns_typetab[58].name; + case DNS_T_AXFR: return dns_typetab[59].name; + case DNS_T_MAILB: return dns_typetab[60].name; + case DNS_T_MAILA: return dns_typetab[61].name; + case DNS_T_ANY: return dns_typetab[62].name; + case DNS_T_ZXFR: return dns_typetab[63].name; + case DNS_T_DLV: return dns_typetab[64].name; + case DNS_T_MAX: return dns_typetab[65].name; + } + return _dns_format_code(nm,"type",code); +} + +const struct dns_nameval dns_classtab[] = { + {DNS_C_INVALID,"INVALID"}, + {DNS_C_IN,"IN"}, + {DNS_C_CH,"CH"}, + {DNS_C_HS,"HS"}, + {DNS_C_ANY,"ANY"}, + {0,0}}; +const char *dns_classname(enum dns_class code) { + static char nm[20]; + switch(code) { + case DNS_C_INVALID: return dns_classtab[0].name; + case DNS_C_IN: return dns_classtab[1].name; + case DNS_C_CH: return dns_classtab[2].name; + case DNS_C_HS: return dns_classtab[3].name; + case DNS_C_ANY: return dns_classtab[4].name; + } + return _dns_format_code(nm,"class",code); +} + +const struct dns_nameval dns_rcodetab[] = { + {DNS_R_NOERROR,"NOERROR"}, + {DNS_R_FORMERR,"FORMERR"}, + {DNS_R_SERVFAIL,"SERVFAIL"}, + {DNS_R_NXDOMAIN,"NXDOMAIN"}, + {DNS_R_NOTIMPL,"NOTIMPL"}, + {DNS_R_REFUSED,"REFUSED"}, + {DNS_R_YXDOMAIN,"YXDOMAIN"}, + {DNS_R_YXRRSET,"YXRRSET"}, + {DNS_R_NXRRSET,"NXRRSET"}, + {DNS_R_NOTAUTH,"NOTAUTH"}, + {DNS_R_NOTZONE,"NOTZONE"}, + {DNS_R_BADSIG,"BADSIG"}, + {DNS_R_BADKEY,"BADKEY"}, + {DNS_R_BADTIME,"BADTIME"}, + {0,0}}; +const char *dns_rcodename(enum dns_rcode code) { + static char nm[20]; + switch(code) { + case DNS_R_NOERROR: return dns_rcodetab[0].name; + case DNS_R_FORMERR: return dns_rcodetab[1].name; + case DNS_R_SERVFAIL: return dns_rcodetab[2].name; + case DNS_R_NXDOMAIN: return dns_rcodetab[3].name; + case DNS_R_NOTIMPL: return dns_rcodetab[4].name; + case DNS_R_REFUSED: return dns_rcodetab[5].name; + case DNS_R_YXDOMAIN: return dns_rcodetab[6].name; + case DNS_R_YXRRSET: return dns_rcodetab[7].name; + case DNS_R_NXRRSET: return dns_rcodetab[8].name; + case DNS_R_NOTAUTH: return dns_rcodetab[9].name; + case DNS_R_NOTZONE: return dns_rcodetab[10].name; + case DNS_R_BADSIG: return dns_rcodetab[11].name; + case DNS_R_BADKEY: return dns_rcodetab[12].name; + case DNS_R_BADTIME: return dns_rcodetab[13].name; + } + return _dns_format_code(nm,"rcode",code); +} diff --git a/ami_test_client/libc-jss/udns/udns_dn.c b/ami_test_client/libc-jss/udns/udns_dn.c new file mode 100644 index 0000000000..ae3fd17788 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_dn.c @@ -0,0 +1,379 @@ +/* udns_dn.c + domain names manipulation routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include "udns.h" + +unsigned dns_dnlen(dnscc_t *dn) { + register dnscc_t *d = dn; + while(*d) + d += 1 + *d; + return (unsigned)(d - dn) + 1; +} + +unsigned dns_dnlabels(register dnscc_t *dn) { + register unsigned l = 0; + while(*dn) + ++l, dn += 1 + *dn; + return l; +} + +unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) { + register unsigned c; + dnscc_t *dn = dn1; + for(;;) { + if ((c = *dn1++) != *dn2++) + return 0; + if (!c) + return (unsigned)(dn1 - dn); + while(c--) { + if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2)) + return 0; + ++dn1; ++dn2; + } + } +} + +unsigned +dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) { + unsigned sdnlen = dns_dnlen(sdn); + if (ddnsiz < sdnlen) + return 0; + memcpy(ddn, sdn, sdnlen); + return sdnlen; +} + +int +dns_ptodn(const char *name, unsigned namelen, + dnsc_t *dn, unsigned dnsiz, int *isabs) +{ + dnsc_t *dp; /* current position in dn (len byte first) */ + dnsc_t *const de /* end of dn: last byte that can be filled up */ + = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1; + dnscc_t *np = (dnscc_t *)name; + dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np)); + dnsc_t *llab; /* start of last label (llab[-1] will be length) */ + unsigned c; /* next input character, or length of last label */ + + if (!dnsiz) + return 0; + dp = llab = dn + 1; + + while(np < ne) { + + if (*np == '.') { /* label delimiter */ + c = dp - llab; /* length of the label */ + if (!c) { /* empty label */ + if (np == (dnscc_t *)name && np + 1 == ne) { + /* special case for root dn, aka `.' */ + ++np; + break; + } + return -1; /* zero label */ + } + if (c > DNS_MAXLABEL) + return -1; /* label too long */ + llab[-1] = (dnsc_t)c; /* update len of last label */ + llab = ++dp; /* start new label, llab[-1] will be len of it */ + ++np; + continue; + } + + /* check whenever we may put out one more byte */ + if (dp >= de) /* too long? */ + return dnsiz >= DNS_MAXDN ? -1 : 0; + if (*np != '\\') { /* non-escape, simple case */ + *dp++ = *np++; + continue; + } + /* handle \-style escape */ + /* note that traditionally, domain names (gethostbyname etc) + * used decimal \dd notation, not octal \ooo (RFC1035), so + * we're following this tradition here. + */ + if (++np == ne) + return -1; /* bad escape */ + else if (*np >= '0' && *np <= '9') { /* decimal number */ + /* we allow not only exactly 3 digits as per RFC1035, + * but also 2 or 1, for better usability. */ + c = *np++ - '0'; + if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */ + c = c * 10 + *np++ - '0'; + if (np < ne && *np >= '0' && *np <= '9') { + c = c * 10 + *np++ - '0'; + if (c > 255) + return -1; /* bad escape */ + } + } + } + else + c = *np++; + *dp++ = (dnsc_t)c; /* place next out byte */ + } + + if ((c = dp - llab) > DNS_MAXLABEL) + return -1; /* label too long */ + if ((llab[-1] = (dnsc_t)c) != 0) { + *dp++ = 0; + if (isabs) + *isabs = 0; + } + else if (isabs) + *isabs = 1; + + return dp - dn; +} + +dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa"; + +dnsc_t * +dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) { + const unsigned char *s = ((const unsigned char *)addr) + 4; + while(s > (const unsigned char *)addr) { + unsigned n = *--s; + dnsc_t *p = dn + 1; + if (n > 99) { + if (p + 2 > dne) return 0; + *p++ = n / 100 + '0'; + *p++ = (n % 100 / 10) + '0'; + *p = n % 10 + '0'; + } + else if (n > 9) { + if (p + 1 > dne) return 0; + *p++ = n / 10 + '0'; + *p = n % 10 + '0'; + } + else { + if (p > dne) return 0; + *p = n + '0'; + } + *dn = p - dn; + dn = p + 1; + } + return dn; +} + +int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); + dnsc_t *p; + unsigned l; + p = dns_a4todn_(addr, dn, dne); + if (!p) return 0; + if (!tdn) + tdn = dns_inaddr_arpa_dn; + l = dns_dnlen(tdn); + if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; + memcpy(p, tdn, l); + return (p + l) - dn; +} + +int dns_a4ptodn(const struct in_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *p; + int r; + if (!tname) + return dns_a4todn(addr, NULL, dn, dnsiz); + p = dns_a4todn_(addr, dn, dn + dnsiz); + if (!p) return 0; + r = dns_sptodn(tname, p, dnsiz - (p - dn)); + return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; +} + +dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa"; + +dnsc_t * +dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) { + const unsigned char *s = ((const unsigned char *)addr) + 16; + if (dn + 64 > dne) return 0; + while(s > (const unsigned char *)addr) { + unsigned n = *--s & 0x0f; + *dn++ = 1; + *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; + *dn++ = 1; + n = *s >> 4; + *dn++ = n > 9 ? n + 'a' - 10 : n + '0'; + } + return dn; +} + +int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz); + dnsc_t *p; + unsigned l; + p = dns_a6todn_(addr, dn, dne); + if (!p) return 0; + if (!tdn) + tdn = dns_ip6_arpa_dn; + l = dns_dnlen(tdn); + if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0; + memcpy(p, tdn, l); + return (p + l) - dn; +} + +int dns_a6ptodn(const struct in6_addr *addr, const char *tname, + dnsc_t *dn, unsigned dnsiz) { + dnsc_t *p; + int r; + if (!tname) + return dns_a6todn(addr, NULL, dn, dnsiz); + p = dns_a6todn_(addr, dn, dn + dnsiz); + if (!p) return 0; + r = dns_sptodn(tname, p, dnsiz - (p - dn)); + return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0; +} + +/* return size of buffer required to convert the dn into asciiz string. + * Keep in sync with dns_dntop() below. + */ +unsigned dns_dntop_size(dnscc_t *dn) { + unsigned size = 0; /* the size reqd */ + dnscc_t *le; /* label end */ + + while(*dn) { + /* *dn is the length of the next label, non-zero */ + if (size) + ++size; /* for the dot */ + le = dn + *dn + 1; + ++dn; + do { + switch(*dn) { + case '.': + case '\\': + /* Special modifiers in zone files. */ + case '"': + case ';': + case '@': + case '$': + size += 2; + break; + default: + if (*dn <= 0x20 || *dn >= 0x7f) + /* \ddd decimal notation */ + size += 4; + else + size += 1; + } + } while(++dn < le); + } + size += 1; /* zero byte at the end - string terminator */ + return size > DNS_MAXNAME ? 0 : size; +} + +/* Convert the dn into asciiz string. + * Keep in sync with dns_dntop_size() above. + */ +int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) { + char *np = name; /* current name ptr */ + char *const ne = name + namesiz; /* end of name */ + dnscc_t *le; /* label end */ + + while(*dn) { + /* *dn is the length of the next label, non-zero */ + if (np != name) { + if (np >= ne) goto toolong; + *np++ = '.'; + } + le = dn + *dn + 1; + ++dn; + do { + switch(*dn) { + case '.': + case '\\': + /* Special modifiers in zone files. */ + case '"': + case ';': + case '@': + case '$': + if (np + 2 > ne) goto toolong; + *np++ = '\\'; + *np++ = *dn; + break; + default: + if (*dn <= 0x20 || *dn >= 0x7f) { + /* \ddd decimal notation */ + if (np + 4 >= ne) goto toolong; + *np++ = '\\'; + *np++ = '0' + (*dn / 100); + *np++ = '0' + ((*dn % 100) / 10); + *np++ = '0' + (*dn % 10); + } + else { + if (np >= ne) goto toolong; + *np++ = *dn; + } + } + } while(++dn < le); + } + if (np >= ne) goto toolong; + *np++ = '\0'; + return np - name; +toolong: + return namesiz >= DNS_MAXNAME ? -1 : 0; +} + +#ifdef TEST +#include +#include + +int main(int argc, char **argv) { + int i; + int sz; + dnsc_t dn[DNS_MAXDN+10]; + dnsc_t *dl, *dp; + int isabs; + + sz = (argc > 1) ? atoi(argv[1]) : 0; + + for(i = 2; i < argc; ++i) { + int r = dns_ptodn(argv[i], 0, dn, sz, &isabs); + printf("%s: ", argv[i]); + if (r < 0) printf("error\n"); + else if (!r) printf("buffer too small\n"); + else { + printf("len=%d dnlen=%d size=%d name:", + r, dns_dnlen(dn), dns_dntop_size(dn)); + dl = dn; + while(*dl) { + printf(" %d=", *dl); + dp = dl + 1; + dl = dp + *dl; + while(dp < dl) { + if (*dp <= ' ' || *dp >= 0x7f) + printf("\\%03d", *dp); + else if (*dp == '.' || *dp == '\\') + printf("\\%c", *dp); + else + putchar(*dp); + ++dp; + } + } + if (isabs) putchar('.'); + putchar('\n'); + } + } + return 0; +} + +#endif /* TEST */ diff --git a/ami_test_client/libc-jss/udns/udns_dntosp.c b/ami_test_client/libc-jss/udns/udns_dntosp.c new file mode 100644 index 0000000000..823fde211e --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_dntosp.c @@ -0,0 +1,30 @@ +/* udns_dntosp.c + dns_dntosp() = convert DN to asciiz string using static buffer + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" + +static char name[DNS_MAXNAME]; + +const char *dns_dntosp(dnscc_t *dn) { + return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0; +} diff --git a/ami_test_client/libc-jss/udns/udns_init.c b/ami_test_client/libc-jss/udns/udns_init.c new file mode 100644 index 0000000000..493af58917 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_init.c @@ -0,0 +1,231 @@ +/* udns_init.c + resolver initialisation stuff + + Copyright (C) 2006 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +# include /* includes */ +# include /* for dns server addresses etc */ +#else +# include +# include +# include +#endif /* !WINDOWS */ + +#include +#include +#include "udns.h" + +#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') + +static const char space[] = " \t\r\n"; + +static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) { + dns_add_serv(ctx, NULL); + for(serv = strtok(serv, space); serv; serv = strtok(NULL, space)) + dns_add_serv(ctx, serv); +} + +static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) { + dns_add_srch(ctx, NULL); + for(srch = strtok(srch, space); srch; srch = strtok(NULL, space)) + dns_add_srch(ctx, srch); +} + +#ifdef WINDOWS + +#ifndef NO_IPHLPAPI +/* Apparently, some systems does not have proper headers for IPHLPAIP to work. + * The best is to upgrade headers, but here's another, ugly workaround for + * this: compile with -DNO_IPHLPAPI. + */ + +typedef DWORD (WINAPI *GetAdaptersAddressesFunc)( + ULONG Family, DWORD Flags, PVOID Reserved, + PIP_ADAPTER_ADDRESSES pAdapterAddresses, + PULONG pOutBufLen); + +static int dns_initns_iphlpapi(struct dns_ctx *ctx) { + HANDLE h_iphlpapi; + GetAdaptersAddressesFunc pfnGetAdAddrs; + PIP_ADAPTER_ADDRESSES pAddr, pAddrBuf; + PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr; + ULONG ulOutBufLen; + DWORD dwRetVal; + int ret = -1; + + h_iphlpapi = LoadLibrary("iphlpapi.dll"); + if (!h_iphlpapi) + return -1; + pfnGetAdAddrs = (GetAdaptersAddressesFunc) + GetProcAddress(h_iphlpapi, "GetAdaptersAddresses"); + if (!pfnGetAdAddrs) goto freelib; + ulOutBufLen = 0; + dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, NULL, &ulOutBufLen); + if (dwRetVal != ERROR_BUFFER_OVERFLOW) goto freelib; + pAddrBuf = malloc(ulOutBufLen); + if (!pAddrBuf) goto freelib; + dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, pAddrBuf, &ulOutBufLen); + if (dwRetVal != ERROR_SUCCESS) goto freemem; + for (pAddr = pAddrBuf; pAddr; pAddr = pAddr->Next) + for (pDnsAddr = pAddr->FirstDnsServerAddress; + pDnsAddr; + pDnsAddr = pDnsAddr->Next) + dns_add_serv_s(ctx, pDnsAddr->Address.lpSockaddr); + ret = 0; +freemem: + free(pAddrBuf); +freelib: + FreeLibrary(h_iphlpapi); + return ret; +} + +#else /* NO_IPHLPAPI */ + +#define dns_initns_iphlpapi(ctx) (-1) + +#endif /* NO_IPHLPAPI */ + +static int dns_initns_registry(struct dns_ctx *ctx) { + LONG res; + HKEY hk; + DWORD type = REG_EXPAND_SZ | REG_SZ; + DWORD len; + char valBuf[1024]; + +#define REGKEY_WINNT "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" +#define REGKEY_WIN9x "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP" + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINNT, 0, KEY_QUERY_VALUE, &hk); + if (res != ERROR_SUCCESS) + res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WIN9x, + 0, KEY_QUERY_VALUE, &hk); + if (res != ERROR_SUCCESS) + return -1; + len = sizeof(valBuf) - 1; + res = RegQueryValueEx(hk, "NameServer", NULL, &type, (BYTE*)valBuf, &len); + if (res != ERROR_SUCCESS || !len || !valBuf[0]) { + len = sizeof(valBuf) - 1; + res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type, + (BYTE*)valBuf, &len); + } + RegCloseKey(hk); + if (res != ERROR_SUCCESS || !len || !valBuf[0]) + return -1; + valBuf[len] = '\0'; + /* nameservers are stored as a whitespace-seperate list: + * "192.168.1.1 123.21.32.12" */ + dns_set_serv_internal(ctx, valBuf); + return 0; +} + +#else /* !WINDOWS */ + +static int dns_init_resolvconf(struct dns_ctx *ctx) { + char *v; + char buf[2049]; /* this buffer is used to hold /etc/resolv.conf */ + int has_srch = 0; + + /* read resolv.conf... */ + { int fd = open("/etc/resolv.conf", O_RDONLY); + if (fd >= 0) { + int l = read(fd, buf, sizeof(buf) - 1); + close(fd); + buf[l < 0 ? 0 : l] = '\0'; + } + else + buf[0] = '\0'; + } + if (buf[0]) { /* ...and parse it */ + char *line, *nextline; + line = buf; + do { + nextline = strchr(line, '\n'); + if (nextline) *nextline++ = '\0'; + v = line; + while(*v && !ISSPACE(*v)) ++v; + if (!*v) continue; + *v++ = '\0'; + while(ISSPACE(*v)) ++v; + if (!*v) continue; + if (strcmp(line, "domain") == 0) { + dns_set_srch_internal(ctx, strtok(v, space)); + has_srch = 1; + } + else if (strcmp(line, "search") == 0) { + dns_set_srch_internal(ctx, v); + has_srch = 1; + } + else if (strcmp(line, "nameserver") == 0) + dns_add_serv(ctx, strtok(v, space)); + else if (strcmp(line, "options") == 0) + dns_set_opts(ctx, v); + } while((line = nextline) != NULL); + } + + buf[sizeof(buf)-1] = '\0'; + + /* get list of nameservers from env. vars. */ + if ((v = getenv("NSCACHEIP")) != NULL || + (v = getenv("NAMESERVERS")) != NULL) { + strncpy(buf, v, sizeof(buf) - 1); + dns_set_serv_internal(ctx, buf); + } + /* if $LOCALDOMAIN is set, use it for search list */ + if ((v = getenv("LOCALDOMAIN")) != NULL) { + strncpy(buf, v, sizeof(buf) - 1); + dns_set_srch_internal(ctx, buf); + has_srch = 1; + } + if ((v = getenv("RES_OPTIONS")) != NULL) + dns_set_opts(ctx, v); + + /* if still no search list, use local domain name */ + if (has_srch && + gethostname(buf, sizeof(buf) - 1) == 0 && + (v = strchr(buf, '.')) != NULL && + *++v != '\0') + dns_add_srch(ctx, v); + + return 0; +} + +#endif /* !WINDOWS */ + +int dns_init(struct dns_ctx *ctx, int do_open) { + if (!ctx) + ctx = &dns_defctx; + dns_reset(ctx); + +#ifdef WINDOWS + if (dns_initns_iphlpapi(ctx) != 0) + dns_initns_registry(ctx); + /*XXX WINDOWS: probably good to get default domain and search list too... + * And options. Something is in registry. */ + /*XXX WINDOWS: maybe environment variables are also useful? */ +#else + dns_init_resolvconf(ctx); +#endif + + return do_open ? dns_open(ctx) : 0; +} diff --git a/ami_test_client/libc-jss/udns/udns_jran.c b/ami_test_client/libc-jss/udns/udns_jran.c new file mode 100644 index 0000000000..19f9d02bb1 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_jran.c @@ -0,0 +1,52 @@ +/* udns_jran.c: small non-cryptographic random number generator + * taken from http://burtleburtle.net/bob/rand/smallprng.html + * by Bob Jenkins, Public domain. + */ + +#include "udns.h" + +#define rot32(x,k) (((x) << (k)) | ((x) >> (32-(k)))) +#define rot64(x,k) (((x) << (k)) | ((x) >> (64-(k)))) +#define tr32(x) ((x)&0xffffffffu) + +unsigned udns_jranval(struct udns_jranctx *x) { + /* This routine can be made to work with either 32 or 64bit words - + * if JRAN_32_64 is defined when compiling the file. + * We use if() instead of #if since there's no good + * portable way to check sizeof() in preprocessor without + * introducing some ugly configure-time checks. + * Most compilers will optimize the wrong branches away anyway. + * By default it assumes 32bit integers + */ +#ifdef JRAN_32_64 + if (sizeof(unsigned) == 4) { +#endif + unsigned e = tr32(x->a - rot32(x->b, 27)); + x->a = tr32(x->b ^ rot32(x->c, 17)); + x->b = tr32(x->c + x->d); + x->c = tr32(x->d + e); + x->d = tr32(e + x->a); +#ifdef JRAN_32_64 + } + else if (sizeof(unsigned) == 8) { /* assuming it's 64bits */ + unsigned e = x->a - rot64(x->b, 7); + x->a = x->b ^ rot64(x->c, 13); + x->b = x->c + rot64(x->d, 37); + x->c = x->d + e; + x->d = e + x->a; + } + else { + unsigned e = 0; + x->d = 1/e; /* bail */ + } +#endif + return x->d; +} + +void udns_jraninit(struct udns_jranctx *x, unsigned seed) { + unsigned i; + x->a = 0xf1ea5eed; + x->b = x->c = x->d = seed; + for (i = 0; i < 20; ++i) + (void)udns_jranval(x); +} diff --git a/ami_test_client/libc-jss/udns/udns_misc.c b/ami_test_client/libc-jss/udns/udns_misc.c new file mode 100644 index 0000000000..c162e70e77 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_misc.c @@ -0,0 +1,67 @@ +/* udns_misc.c + miscellaneous routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include "udns.h" + +int dns_findname(const struct dns_nameval *nv, const char *name) { + register const char *a, *b; + for(; nv->name; ++nv) + for(a = name, b = nv->name; ; ++a, ++b) + if (DNS_DNUC(*a) != *b) break; + else if (!*a) return nv->val; + return -1; +} + +const char *_dns_format_code(char *buf, const char *prefix, int code) { + char *bp = buf; + unsigned c, n; + do *bp++ = DNS_DNUC(*prefix); + while(*++prefix); + *bp++ = '#'; + if (code < 0) code = -code, *bp++ = '-'; + n = 0; c = code; + do ++n; + while((c /= 10)); + c = code; + bp[n--] = '\0'; + do bp[n--] = c % 10 + '0'; + while((c /= 10)); + return buf; +} + +const char *dns_strerror(int err) { + if (err >= 0) return "successeful completion"; + switch(err) { + case DNS_E_TEMPFAIL: return "temporary failure in name resolution"; + case DNS_E_PROTOCOL: return "protocol error"; + case DNS_E_NXDOMAIN: return "domain name does not exist"; + case DNS_E_NODATA: return "valid domain but no data of requested type"; + case DNS_E_NOMEM: return "out of memory"; + case DNS_E_BADQUERY: return "malformed query"; + default: return "unknown error"; + } +} + +const char *dns_version(void) { + return UDNS_VERSION; +} diff --git a/ami_test_client/libc-jss/udns/udns_parse.c b/ami_test_client/libc-jss/udns/udns_parse.c new file mode 100644 index 0000000000..8924b1562d --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_parse.c @@ -0,0 +1,169 @@ +/* udns_parse.c + raw DNS packet parsing routines + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include "udns.h" + +dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) { + unsigned c; + for(;;) { + if (cur >= end) + return NULL; + c = *cur++; + if (!c) + return cur; + if (c & 192) /* jump */ + return cur + 1 >= end ? NULL : cur + 1; + cur += c; + } +} + +int +dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end, + register dnsc_t *dn, unsigned dnsiz) { + unsigned c; + dnscc_t *pp = *cur; /* current packet pointer */ + dnsc_t *dp = dn; /* current dn pointer */ + dnsc_t *const de /* end of the DN dest */ + = dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN); + dnscc_t *jump = NULL; /* ptr after first jump if any */ + unsigned loop = 100; /* jump loop counter */ + + for(;;) { /* loop by labels */ + if (pp >= end) /* reached end of packet? */ + return -1; + c = *pp++; /* length of the label */ + if (!c) { /* empty label: terminate */ + if (dn >= de) /* can't fit terminator */ + goto noroom; + *dp++ = 0; + /* return next pos: either after the first jump or current */ + *cur = jump ? jump : pp; + return dp - dn; + } + if (c & 192) { /* jump */ + if (pp >= end) /* eop instead of jump pos */ + return -1; + if (!jump) jump = pp + 1; /* remember first jump */ + else if (!--loop) return -1; /* too many jumps */ + c = ((c & ~192) << 8) | *pp; /* new pos */ + if (c < DNS_HSIZE) /* don't allow jump into the header */ + return -1; + pp = pkt + c; + continue; + } + if (c > DNS_MAXLABEL) /* too long label? */ + return -1; + if (pp + c > end) /* label does not fit in packet? */ + return -1; + if (dp + c + 1 > de) /* if enouth room for the label */ + goto noroom; + *dp++ = c; /* label length */ + memcpy(dp, pp, c); /* and the label itself */ + dp += c; + pp += c; /* advance to the next label */ + } +noroom: + return dnsiz < DNS_MAXDN ? 0 : -1; +} + +void dns_rewind(struct dns_parse *p, dnscc_t *qdn) { + p->dnsp_qdn = qdn; + p->dnsp_cur = p->dnsp_ans; + p->dnsp_rrl = dns_numan(p->dnsp_pkt); + p->dnsp_ttl = 0xffffffffu; + p->dnsp_nrr = 0; +} + +void +dns_initparse(struct dns_parse *p, dnscc_t *qdn, + dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) { + p->dnsp_pkt = pkt; + p->dnsp_end = end; + p->dnsp_rrl = dns_numan(pkt); + p->dnsp_qdn = qdn; + assert(cur + 4 <= end); + if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0; + if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0; + p->dnsp_cur = p->dnsp_ans = cur + 4; + p->dnsp_ttl = 0xffffffffu; + p->dnsp_nrr = 0; +} + +int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) { + dnscc_t *cur = p->dnsp_cur; + while(p->dnsp_rrl > 0) { + --p->dnsp_rrl; + if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end, + rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0) + return -1; + if (cur + 10 > p->dnsp_end) + return -1; + rr->dnsrr_typ = dns_get16(cur); + rr->dnsrr_cls = dns_get16(cur+2); + rr->dnsrr_ttl = dns_get32(cur+4); + rr->dnsrr_dsz = dns_get16(cur+8); + rr->dnsrr_dptr = cur = cur + 10; + rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz; + if (cur > p->dnsp_end) + return -1; + if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn)) + continue; + if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) && + (!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) { + p->dnsp_cur = cur; + ++p->dnsp_nrr; + if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; + return 1; + } + if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) { + if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end, + p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 || + rr->dnsrr_dptr != rr->dnsrr_dend) + return -1; + p->dnsp_qdn = p->dnsp_dnbuf; + if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; + } + } + p->dnsp_cur = cur; + return 0; +} + +int dns_stdrr_size(const struct dns_parse *p) { + return + dns_dntop_size(p->dnsp_qdn) + + (p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 : + dns_dntop_size(dns_payload(p->dnsp_pkt))); +} + +void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp, + const struct dns_parse *p) { + cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME); + if (p->dnsp_qdn == dns_payload(p->dnsp_pkt)) + ret->dnsn_qname = ret->dnsn_cname; + else + dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME); + ret->dnsn_ttl = p->dnsp_ttl; + return ret; +} diff --git a/ami_test_client/libc-jss/udns/udns_resolver.c b/ami_test_client/libc-jss/udns/udns_resolver.c new file mode 100644 index 0000000000..b8f899a246 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_resolver.c @@ -0,0 +1,1323 @@ +/* udns_resolver.c + resolver stuff (main module) + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef WINDOWS +# include /* includes */ +# include /* needed for struct in6_addr */ +#else +# include +# include +# include +# include +# include +# include +# ifdef HAVE_POLL +# include +# else +# ifdef HAVE_SYS_SELECT_H +# include +# endif +# endif +# ifdef HAVE_TIMES +# include +# endif +# define closesocket(sock) close(sock) +#endif /* !WINDOWS */ + +#include +#include +#include +#include +#include +#include +#include "udns.h" + +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT EINVAL +#endif +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif + +struct dns_qlist { + struct dns_query *head, *tail; +}; + +struct dns_query { + struct dns_query *dnsq_next; /* double-linked list */ + struct dns_query *dnsq_prev; + unsigned dnsq_origdnl0; /* original query DN len w/o last 0 */ + unsigned dnsq_flags; /* control flags for this query */ + unsigned dnsq_servi; /* index of next server to try */ + unsigned dnsq_servwait; /* bitmask: servers left to wait */ + unsigned dnsq_servskip; /* bitmask: servers to skip */ + unsigned dnsq_servnEDNS0; /* bitmask: servers refusing EDNS0 */ + unsigned dnsq_try; /* number of tries made so far */ + dnscc_t *dnsq_nxtsrch; /* next search pointer @dnsc_srchbuf */ + time_t dnsq_deadline; /* when current try will expire */ + dns_parse_fn *dnsq_parse; /* parse: raw => application */ + dns_query_fn *dnsq_cbck; /* the callback to call when done */ + void *dnsq_cbdata; /* user data for the callback */ +#ifndef NDEBUG + struct dns_ctx *dnsq_ctx; /* the resolver context */ +#endif + /* char fields at the end to avoid padding */ + dnsc_t dnsq_id[2]; /* query ID */ + dnsc_t dnsq_typcls[4]; /* requested RR type+class */ + dnsc_t dnsq_dn[DNS_MAXDN+DNS_DNPAD]; /* the query DN +alignment */ +}; + +/* working with dns_query lists */ + +static __inline void qlist_init(struct dns_qlist *list) { + list->head = list->tail = NULL; +} + +static __inline void qlist_remove(struct dns_qlist *list, struct dns_query *q) { + if (q->dnsq_prev) q->dnsq_prev->dnsq_next = q->dnsq_next; + else list->head = q->dnsq_next; + if (q->dnsq_next) q->dnsq_next->dnsq_prev = q->dnsq_prev; + else list->tail = q->dnsq_prev; +} + +static __inline void +qlist_add_head(struct dns_qlist *list, struct dns_query *q) { + q->dnsq_next = list->head; + if (list->head) list->head->dnsq_prev = q; + else list->tail = q; + list->head = q; + q->dnsq_prev = NULL; +} + +static __inline void +qlist_insert_after(struct dns_qlist *list, + struct dns_query *q, struct dns_query *prev) { + if ((q->dnsq_prev = prev) != NULL) { + if ((q->dnsq_next = prev->dnsq_next) != NULL) + q->dnsq_next->dnsq_prev = q; + else + list->tail = q; + prev->dnsq_next = q; + } + else + qlist_add_head(list, q); +} + +union sockaddr_ns { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef HAVE_IPv6 + struct sockaddr_in6 sin6; +#endif +}; + +#define sin_eq(a,b) \ + ((a).sin_port == (b).sin_port && \ + (a).sin_addr.s_addr == (b).sin_addr.s_addr) +#define sin6_eq(a,b) \ + ((a).sin6_port == (b).sin6_port && \ + memcmp(&(a).sin6_addr, &(b).sin6_addr, sizeof(struct in6_addr)) == 0) + +struct dns_ctx { /* resolver context */ + /* settings */ + unsigned dnsc_flags; /* various flags */ + unsigned dnsc_timeout; /* timeout (base value) for queries */ + unsigned dnsc_ntries; /* number of retries */ + unsigned dnsc_ndots; /* ndots to assume absolute name */ + unsigned dnsc_port; /* default port (DNS_PORT) */ + unsigned dnsc_udpbuf; /* size of UDP buffer */ + /* array of nameserver addresses */ + union sockaddr_ns dnsc_serv[DNS_MAXSERV]; + unsigned dnsc_nserv; /* number of nameservers */ + unsigned dnsc_salen; /* length of socket addresses */ + dnsc_t dnsc_srchbuf[1024]; /* buffer for searchlist */ + dnsc_t *dnsc_srchend; /* current end of srchbuf */ + + dns_utm_fn *dnsc_utmfn; /* register/cancel timer events */ + void *dnsc_utmctx; /* user timer context for utmfn() */ + time_t dnsc_utmexp; /* when user timer expires */ + + dns_dbgfn *dnsc_udbgfn; /* debugging function */ + + /* dynamic data */ + struct udns_jranctx dnsc_jran; /* random number generator state */ + unsigned dnsc_nextid; /* next queue ID to use if !0 */ + int dnsc_udpsock; /* UDP socket */ + struct dns_qlist dnsc_qactive; /* active list sorted by deadline */ + int dnsc_nactive; /* number entries in dnsc_qactive */ + dnsc_t *dnsc_pbuf; /* packet buffer (udpbuf size) */ + int dnsc_qstatus; /* last query status value */ +}; + +static const struct { + const char *name; + enum dns_opt opt; + unsigned offset; + unsigned min, max; +} dns_opts[] = { +#define opt(name,opt,field,min,max) \ + {name,opt,offsetof(struct dns_ctx,field),min,max} + opt("retrans", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), + opt("timeout", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300), + opt("retry", DNS_OPT_NTRIES, dnsc_ntries, 1,50), + opt("attempts", DNS_OPT_NTRIES, dnsc_ntries, 1,50), + opt("ndots", DNS_OPT_NDOTS, dnsc_ndots, 0,1000), + opt("port", DNS_OPT_PORT, dnsc_port, 1,0xffff), + opt("udpbuf", DNS_OPT_UDPSIZE, dnsc_udpbuf, DNS_MAXPACKET,65536), +#undef opt +}; +#define dns_ctxopt(ctx,idx) (*((unsigned*)(((char*)ctx)+dns_opts[idx].offset))) + +#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n') + +struct dns_ctx dns_defctx; + +#define SETCTX(ctx) if (!ctx) ctx = &dns_defctx +#define SETCTXINITED(ctx) SETCTX(ctx); assert(CTXINITED(ctx)) +#define CTXINITED(ctx) (ctx->dnsc_flags & DNS_INITED) +#define SETCTXFRESH(ctx) SETCTXINITED(ctx); assert(!CTXOPEN(ctx)) +#define SETCTXINACTIVE(ctx) \ + SETCTXINITED(ctx); assert(!ctx->dnsc_nactive) +#define SETCTXOPEN(ctx) SETCTXINITED(ctx); assert(CTXOPEN(ctx)) +#define CTXOPEN(ctx) (ctx->dnsc_udpsock >= 0) + +#if defined(NDEBUG) || !defined(DEBUG) +#define dns_assert_ctx(ctx) +#else +static void dns_assert_ctx(const struct dns_ctx *ctx) { + int nactive = 0; + const struct dns_query *q; + for(q = ctx->dnsc_qactive.head; q; q = q->dnsq_next) { + assert(q->dnsq_ctx == ctx); + assert(q == (q->dnsq_next ? + q->dnsq_next->dnsq_prev : ctx->dnsc_qactive.tail)); + assert(q == (q->dnsq_prev ? + q->dnsq_prev->dnsq_next : ctx->dnsc_qactive.head)); + ++nactive; + } + assert(nactive == ctx->dnsc_nactive); +} +#endif + +enum { + DNS_INTERNAL = 0xffff, /* internal flags mask */ + DNS_INITED = 0x0001, /* the context is initialized */ + DNS_ASIS_DONE = 0x0002, /* search: skip the last as-is query */ + DNS_SEEN_NODATA = 0x0004, /* search: NODATA has been received */ +}; + +int dns_add_serv(struct dns_ctx *ctx, const char *serv) { + union sockaddr_ns *sns; + SETCTXFRESH(ctx); + if (!serv) + return (ctx->dnsc_nserv = 0); + if (ctx->dnsc_nserv >= DNS_MAXSERV) + return errno = ENFILE, -1; + sns = &ctx->dnsc_serv[ctx->dnsc_nserv]; + memset(sns, 0, sizeof(*sns)); + if (dns_pton(AF_INET, serv, &sns->sin.sin_addr) > 0) { + sns->sin.sin_family = AF_INET; + return ++ctx->dnsc_nserv; + } +#ifdef HAVE_IPv6 + if (dns_pton(AF_INET6, serv, &sns->sin6.sin6_addr) > 0) { + sns->sin6.sin6_family = AF_INET6; + return ++ctx->dnsc_nserv; + } +#endif + errno = EINVAL; + return -1; +} + +int dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) { + SETCTXFRESH(ctx); + if (!sa) + return (ctx->dnsc_nserv = 0); + if (ctx->dnsc_nserv >= DNS_MAXSERV) + return errno = ENFILE, -1; +#ifdef HAVE_IPv6 + else if (sa->sa_family == AF_INET6) + ctx->dnsc_serv[ctx->dnsc_nserv].sin6 = *(struct sockaddr_in6*)sa; +#endif + else if (sa->sa_family == AF_INET) + ctx->dnsc_serv[ctx->dnsc_nserv].sin = *(struct sockaddr_in*)sa; + else + return errno = EAFNOSUPPORT, -1; + return ++ctx->dnsc_nserv; +} + +int dns_set_opts(struct dns_ctx *ctx, const char *opts) { + unsigned i, v; + int err = 0; + SETCTXINACTIVE(ctx); + for(;;) { + while(ISSPACE(*opts)) ++opts; + if (!*opts) break; + for(i = 0; ; ++i) { + if (i >= sizeof(dns_opts)/sizeof(dns_opts[0])) { ++err; break; } + v = strlen(dns_opts[i].name); + if (strncmp(dns_opts[i].name, opts, v) != 0 || + (opts[v] != ':' && opts[v] != '=')) + continue; + opts += v + 1; + v = 0; + if (*opts < '0' || *opts > '9') { ++err; break; } + do v = v * 10 + (*opts++ - '0'); + while (*opts >= '0' && *opts <= '9'); + if (v < dns_opts[i].min) v = dns_opts[i].min; + if (v > dns_opts[i].max) v = dns_opts[i].max; + dns_ctxopt(ctx, i) = v; + break; + } + while(*opts && !ISSPACE(*opts)) ++opts; + } + return err; +} + +int dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val) { + int prev; + unsigned i; + SETCTXINACTIVE(ctx); + for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) { + if (dns_opts[i].opt != opt) continue; + prev = dns_ctxopt(ctx, i); + if (val >= 0) { + unsigned v = val; + if (v < dns_opts[i].min || v > dns_opts[i].max) { + errno = EINVAL; + return -1; + } + dns_ctxopt(ctx, i) = v; + } + return prev; + } + if (opt == DNS_OPT_FLAGS) { + prev = ctx->dnsc_flags & ~DNS_INTERNAL; + if (val >= 0) + ctx->dnsc_flags = + (ctx->dnsc_flags & DNS_INTERNAL) | (val & ~DNS_INTERNAL); + return prev; + } + errno = ENOSYS; + return -1; +} + +int dns_add_srch(struct dns_ctx *ctx, const char *srch) { + int dnl; + SETCTXINACTIVE(ctx); + if (!srch) { + memset(ctx->dnsc_srchbuf, 0, sizeof(ctx->dnsc_srchbuf)); + ctx->dnsc_srchend = ctx->dnsc_srchbuf; + return 0; + } + dnl = + sizeof(ctx->dnsc_srchbuf) - (ctx->dnsc_srchend - ctx->dnsc_srchbuf) - 1; + dnl = dns_sptodn(srch, ctx->dnsc_srchend, dnl); + if (dnl > 0) + ctx->dnsc_srchend += dnl; + ctx->dnsc_srchend[0] = '\0'; /* we ensure the list is always ends at . */ + if (dnl > 0) + return 0; + errno = EINVAL; + return -1; +} + +static void dns_drop_utm(struct dns_ctx *ctx) { + if (ctx->dnsc_utmfn) + ctx->dnsc_utmfn(NULL, -1, ctx->dnsc_utmctx); + ctx->dnsc_utmctx = NULL; + ctx->dnsc_utmexp = -1; +} + +static void +_dns_request_utm(struct dns_ctx *ctx, time_t now) { + struct dns_query *q; + time_t deadline; + int timeout; + q = ctx->dnsc_qactive.head; + if (!q) + deadline = -1, timeout = -1; + else if (!now || q->dnsq_deadline <= now) + deadline = 0, timeout = 0; + else + deadline = q->dnsq_deadline, timeout = (int)(deadline - now); + if (ctx->dnsc_utmexp == deadline) + return; + ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx); + ctx->dnsc_utmexp = deadline; +} + +static __inline void +dns_request_utm(struct dns_ctx *ctx, time_t now) { + if (ctx->dnsc_utmfn) + _dns_request_utm(ctx, now); +} + +void dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn) { + SETCTXINITED(ctx); + ctx->dnsc_udbgfn = dbgfn; +} + +void +dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) { + SETCTXINITED(ctx); + dns_drop_utm(ctx); + ctx->dnsc_utmfn = fn; + ctx->dnsc_utmctx = data; + if (CTXOPEN(ctx)) + dns_request_utm(ctx, 0); +} + +static unsigned dns_nonrandom_32(void) { +#ifdef WINDOWS + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return ft.dwLowDateTime; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec; +#endif +} + +/* This is historic deprecated API */ +UDNS_API unsigned dns_random16(void); +unsigned dns_random16(void) { + unsigned x = dns_nonrandom_32(); + return (x ^ (x >> 16)) & 0xffff; +} + +static void dns_init_rng(struct dns_ctx *ctx) { + udns_jraninit(&ctx->dnsc_jran, dns_nonrandom_32()); + ctx->dnsc_nextid = 0; +} + +void dns_close(struct dns_ctx *ctx) { + struct dns_query *q, *p; + SETCTX(ctx); + if (CTXINITED(ctx)) { + if (ctx->dnsc_udpsock >= 0) + closesocket(ctx->dnsc_udpsock); + ctx->dnsc_udpsock = -1; + if (ctx->dnsc_pbuf) + free(ctx->dnsc_pbuf); + ctx->dnsc_pbuf = NULL; + q = ctx->dnsc_qactive.head; + while((p = q) != NULL) { + q = q->dnsq_next; + free(p); + } + qlist_init(&ctx->dnsc_qactive); + ctx->dnsc_nactive = 0; + dns_drop_utm(ctx); + } +} + +void dns_reset(struct dns_ctx *ctx) { + SETCTX(ctx); + dns_close(ctx); + memset(ctx, 0, sizeof(*ctx)); + ctx->dnsc_timeout = 4; + ctx->dnsc_ntries = 3; + ctx->dnsc_ndots = 1; + ctx->dnsc_udpbuf = DNS_EDNS0PACKET; + ctx->dnsc_port = DNS_PORT; + ctx->dnsc_udpsock = -1; + ctx->dnsc_srchend = ctx->dnsc_srchbuf; + qlist_init(&ctx->dnsc_qactive); + dns_init_rng(ctx); + ctx->dnsc_flags = DNS_INITED; +} + +struct dns_ctx *dns_new(const struct dns_ctx *copy) { + struct dns_ctx *ctx; + SETCTXINITED(copy); + dns_assert_ctx(copy); + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + *ctx = *copy; + ctx->dnsc_udpsock = -1; + qlist_init(&ctx->dnsc_qactive); + ctx->dnsc_nactive = 0; + ctx->dnsc_pbuf = NULL; + ctx->dnsc_qstatus = 0; + ctx->dnsc_srchend = ctx->dnsc_srchbuf + + (copy->dnsc_srchend - copy->dnsc_srchbuf); + ctx->dnsc_utmfn = NULL; + ctx->dnsc_utmctx = NULL; + dns_init_rng(ctx); + return ctx; +} + +void dns_free(struct dns_ctx *ctx) { + assert(ctx != NULL && ctx != &dns_defctx); + dns_reset(ctx); + free(ctx); +} + +int dns_open(struct dns_ctx *ctx) { + int sock; + unsigned i; + int port; + union sockaddr_ns *sns; +#ifdef HAVE_IPv6 + unsigned have_inet6 = 0; +#endif + + SETCTXINITED(ctx); + assert(!CTXOPEN(ctx)); + + port = htons((unsigned short)ctx->dnsc_port); + /* ensure we have at least one server */ + if (!ctx->dnsc_nserv) { + sns = ctx->dnsc_serv; + sns->sin.sin_family = AF_INET; + sns->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ctx->dnsc_nserv = 1; + } + + for (i = 0; i < ctx->dnsc_nserv; ++i) { + sns = &ctx->dnsc_serv[i]; + /* set port for each sockaddr */ +#ifdef HAVE_IPv6 + if (sns->sa.sa_family == AF_INET6) { + if (!sns->sin6.sin6_port) sns->sin6.sin6_port = (unsigned short)port; + ++have_inet6; + } + else +#endif + { + assert(sns->sa.sa_family == AF_INET); + if (!sns->sin.sin_port) sns->sin.sin_port = (unsigned short)port; + } + } + +#ifdef HAVE_IPv6 + if (have_inet6 && have_inet6 < ctx->dnsc_nserv) { + /* convert all IPv4 addresses to IPv6 V4MAPPED */ + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + /* V4MAPPED: ::ffff:1.2.3.4 */ + sin6.sin6_addr.s6_addr[10] = 0xff; + sin6.sin6_addr.s6_addr[11] = 0xff; + for(i = 0; i < ctx->dnsc_nserv; ++i) { + sns = &ctx->dnsc_serv[i]; + if (sns->sa.sa_family == AF_INET) { + sin6.sin6_port = sns->sin.sin_port; + memcpy(sin6.sin6_addr.s6_addr + 4*3, &sns->sin.sin_addr, 4); + sns->sin6 = sin6; + } + } + } + + ctx->dnsc_salen = have_inet6 ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + + if (have_inet6) + sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#else /* !HAVE_IPv6 */ + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + ctx->dnsc_salen = sizeof(struct sockaddr_in); +#endif /* HAVE_IPv6 */ + + if (sock < 0) { + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } +#ifdef WINDOWS + { unsigned long on = 1; + if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } + } +#else /* !WINDOWS */ + if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0 || + fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_TEMPFAIL; + return -1; + } +#endif /* WINDOWS */ + /* allocate the packet buffer */ + if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) { + closesocket(sock); + ctx->dnsc_qstatus = DNS_E_NOMEM; + errno = ENOMEM; + return -1; + } + + ctx->dnsc_udpsock = sock; + dns_request_utm(ctx, 0); + return sock; +} + +int dns_sock(const struct dns_ctx *ctx) { + SETCTXINITED(ctx); + return ctx->dnsc_udpsock; +} + +int dns_active(const struct dns_ctx *ctx) { + SETCTXINITED(ctx); + dns_assert_ctx(ctx); + return ctx->dnsc_nactive; +} + +int dns_status(const struct dns_ctx *ctx) { + SETCTX(ctx); + return ctx->dnsc_qstatus; +} +void dns_setstatus(struct dns_ctx *ctx, int status) { + SETCTX(ctx); + ctx->dnsc_qstatus = status; +} + +/* End the query: disconnect it from the active list, free it, + * and return the result to the caller. + */ +static void +dns_end_query(struct dns_ctx *ctx, struct dns_query *q, + int status, void *result) { + dns_query_fn *cbck = q->dnsq_cbck; + void *cbdata = q->dnsq_cbdata; + ctx->dnsc_qstatus = status; + assert((status < 0 && result == 0) || (status >= 0 && result != 0)); + assert(cbck != 0); /*XXX callback may be NULL */ + assert(ctx->dnsc_nactive > 0); + --ctx->dnsc_nactive; + qlist_remove(&ctx->dnsc_qactive, q); + /* force the query to be unconnected */ + /*memset(q, 0, sizeof(*q));*/ +#ifndef NDEBUG + q->dnsq_ctx = NULL; +#endif + free(q); + cbck(ctx, result, cbdata); +} + +#define DNS_DBG(ctx, code, sa, slen, pkt, plen) \ + do { \ + if (ctx->dnsc_udbgfn) \ + ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, 0, 0); \ + } while(0) +#define DNS_DBGQ(ctx, q, code, sa, slen, pkt, plen) \ + do { \ + if (ctx->dnsc_udbgfn) \ + ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, q, q->dnsq_cbdata); \ + } while(0) + +static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) { + /* this is how we choose an identifier for a new query (qID). + * For now, it's just sequential number, incremented for every query, and + * thus obviously trivial to guess. + * There are two choices: + * a) use sequential numbers. It is plain insecure. In DNS, there are two + * places where random numbers are (or can) be used to increase security: + * random qID and random source port number. Without this randomness + * (udns uses fixed port for all queries), or when the randomness is weak, + * it's trivial to spoof query replies. With randomness however, it + * becomes a bit more difficult task. Too bad we only have 16 bits for + * our security, as qID is only two bytes. It isn't a security per se, + * to rely on those 16 bits - an attacker can just flood us with fake + * replies with all possible qIDs (only 65536 of them), and in this case, + * even if we'll use true random qIDs, we'll be in trouble (not protected + * against spoofing). Yes, this is only possible on a high-speed network + * (probably on the LAN only, since usually a border router for a LAN + * protects internal machines from packets with spoofed local addresses + * from outside, and usually a nameserver resides on LAN), but it's + * still very well possible to send us fake replies. + * In other words: there's nothing a DNS (stub) resolver can do against + * spoofing attacks, unless DNSSEC is in use, which helps here alot. + * Too bad that DNSSEC isn't widespread, so relying on it isn't an + * option in almost all cases... + * b) use random qID, based on some random-number generation mechanism. + * This way, we increase our protection a bit (see above - it's very weak + * still), but we also increase risk of qID reuse and matching late replies + * that comes to queries we've sent before against new queries. There are + * some more corner cases around that, as well - for example, normally, + * udns tries to find the query for a given reply by qID, *and* by + * verifying that the query DN and other parameters are also the same + * (so if the new query is against another domain name, old reply will + * be ignored automatically). But certain types of replies which we now + * handle - for example, FORMERR reply from servers which refuses to + * process EDNS0-enabled packets - comes without all the query parameters + * but the qID - so we're forced to use qID only when determining which + * query the given reply corresponds to. This makes us even more + * vulnerable to spoofing attacks, because an attacker don't even need to + * know which queries we perform to spoof the replies - he only needs to + * flood us with fake FORMERR "replies". + * + * That all to say: using sequential (or any other trivially guessable) + * numbers for qIDs is insecure, but the whole thing is inherently insecure + * as well, and this "extra weakness" that comes from weak qID choosing + * algorithm adds almost nothing to the underlying problem. + * + * It CAN NOT be made secure. Period. That's it. + * Unless we choose to implement DNSSEC, which is a whole different story. + * Forcing TCP mode makes it better, but who uses TCP for DNS anyway? + * (and it's hardly possible because of huge impact on the recursive + * nameservers). + * + * Note that ALL stub resolvers (again, unless they implement and enforce + * DNSSEC) suffers from this same problem. + * + * Here, I use a pseudo-random number generator for qIDs, instead of a + * simpler sequential IDs. This is _not_ more secure than sequential + * ID, but some found random IDs more enjoyeable for some reason. So + * here it goes. + */ + + /* Use random number and check if it's unique. + * If it's not, try again up to 5 times. + */ + unsigned loop; + dnsc_t c0, c1; + for(loop = 0; loop < 5; ++loop) { + const struct dns_query *c; + if (!ctx->dnsc_nextid) + ctx->dnsc_nextid = udns_jranval(&ctx->dnsc_jran); + c0 = ctx->dnsc_nextid & 0xff; + c1 = (ctx->dnsc_nextid >> 8) & 0xff; + ctx->dnsc_nextid >>= 16; + for(c = ctx->dnsc_qactive.head; c; c = c->dnsq_next) + if (c->dnsq_id[0] == c0 && c->dnsq_id[1] == c1) + break; /* found such entry, try again */ + if (!c) + break; + } + q->dnsq_id[0] = c0; q->dnsq_id[1] = c1; + + /* reset all parameters relevant for previous query lifetime */ + q->dnsq_try = 0; + q->dnsq_servi = 0; + /*XXX probably should keep dnsq_servnEDNS0 bits? + * See also comments in dns_ioevent() about FORMERR case */ + q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0; +} + +/* Find next search suffix and fills in q->dnsq_dn. + * Return 0 if no more to try. */ +static int dns_next_srch(struct dns_ctx *ctx, struct dns_query *q) { + unsigned dnl; + + for(;;) { + if (q->dnsq_nxtsrch > ctx->dnsc_srchend) + return 0; + dnl = dns_dnlen(q->dnsq_nxtsrch); + if (dnl + q->dnsq_origdnl0 <= DNS_MAXDN && + (*q->dnsq_nxtsrch || !(q->dnsq_flags & DNS_ASIS_DONE))) + break; + q->dnsq_nxtsrch += dnl; + } + memcpy(q->dnsq_dn + q->dnsq_origdnl0, q->dnsq_nxtsrch, dnl); + if (!*q->dnsq_nxtsrch) + q->dnsq_flags |= DNS_ASIS_DONE; + q->dnsq_nxtsrch += dnl; + dns_newid(ctx, q); /* new ID for new qDN */ + return 1; +} + +/* find the server to try for current iteration. + * Note that current dnsq_servi may point to a server we should skip -- + * in that case advance to the next server. + * Return true if found, false if all tried. + */ +static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) { + while(q->dnsq_servi < ctx->dnsc_nserv) { + if (!(q->dnsq_servskip & (1 << q->dnsq_servi))) + return 1; + ++q->dnsq_servi; + } + return 0; +} + +/* format and send the query to a given server. + * In case of network problem (sendto() fails), return -1, + * else return 0. + */ +static int +dns_send_this(struct dns_ctx *ctx, struct dns_query *q, + unsigned servi, time_t now) { + unsigned qlen; + unsigned tries; + + { /* format the query buffer */ + dnsc_t *p = ctx->dnsc_pbuf; + memset(p, 0, DNS_HSIZE); + if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD; + if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA; + if (q->dnsq_flags & DNS_SET_CD) p[DNS_H_F2] |= DNS_HF2_CD; + p[DNS_H_QDCNT2] = 1; + memcpy(p + DNS_H_QID, q->dnsq_id, 2); + p = dns_payload(p); + /* copy query dn */ + p += dns_dntodn(q->dnsq_dn, p, DNS_MAXDN); + /* query type and class */ + memcpy(p, q->dnsq_typcls, 4); p += 4; + /* add EDNS0 record. DO flag requires it */ + if (q->dnsq_flags & DNS_SET_DO || + (ctx->dnsc_udpbuf > DNS_MAXPACKET && + !(q->dnsq_servnEDNS0 & (1 << servi)))) { + *p++ = 0; /* empty (root) DN */ + p = dns_put16(p, DNS_T_OPT); + p = dns_put16(p, ctx->dnsc_udpbuf); + /* EDNS0 RCODE & VERSION; rest of the TTL field; RDLEN */ + memset(p, 0, 2+2+2); + if (q->dnsq_flags & DNS_SET_DO) p[2] |= DNS_EF1_DO; + p += 2+2+2; + ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1; + } + qlen = p - ctx->dnsc_pbuf; + assert(qlen <= ctx->dnsc_udpbuf); + } + + /* send the query */ + tries = 10; + while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0, + &ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) { + /*XXX just ignore the sendto() error for now and try again. + * In the future, it may be possible to retrieve the error code + * and find which operation/query failed. + *XXX try the next server too? (if ENETUNREACH is returned immediately) + */ + if (--tries) continue; + /* if we can't send the query, fail it. */ + dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); + return -1; + } + DNS_DBGQ(ctx, q, 1, + &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns), + ctx->dnsc_pbuf, qlen); + q->dnsq_servwait |= 1 << servi; /* expect reply from this ns */ + + q->dnsq_deadline = now + + (dns_find_serv(ctx, q) ? 1 : ctx->dnsc_timeout << q->dnsq_try); + + /* move the query to the proper place, according to the new deadline */ + qlist_remove(&ctx->dnsc_qactive, q); + { /* insert from the tail */ + struct dns_query *p; + for(p = ctx->dnsc_qactive.tail; p; p = p->dnsq_prev) + if (p->dnsq_deadline <= q->dnsq_deadline) + break; + qlist_insert_after(&ctx->dnsc_qactive, q, p); + } + + return 0; +} + +/* send the query out using next available server + * and add it to the active list, or, if no servers available, + * end it. + */ +static void +dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) { + + /* if we can't send the query, return TEMPFAIL even when searching: + * we can't be sure whenever the name we tried to search exists or not, + * so don't continue searching, or we may find the wrong name. */ + + if (!dns_find_serv(ctx, q)) { + /* no more servers in this iteration. Try the next cycle */ + q->dnsq_servi = 0; /* reset */ + q->dnsq_try++; /* next try */ + if (q->dnsq_try >= ctx->dnsc_ntries || + !dns_find_serv(ctx, q)) { + /* no more servers and tries, fail the query */ + /* return TEMPFAIL even when searching: no more tries for this + * searchlist, and no single definitive reply (handled in dns_ioevent() + * in NOERROR or NXDOMAIN cases) => all nameservers failed to process + * current search list element, so we don't know whenever the name exists. + */ + dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0); + return; + } + } + + dns_send_this(ctx, q, q->dnsq_servi++, now); +} + +static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) { + if (result) free(result); + data = ctx = 0; /* used */ +} + +/* The (only, main, real) query submission routine. + * Allocate new query structure, initialize it, check validity of + * parameters, and add it to the head of the active list, without + * trying to send it (to be picked up on next event). + * Error return (without calling the callback routine) - + * no memory or wrong parameters. + *XXX The `no memory' case probably should go to the callback anyway... + */ +struct dns_query * +dns_submit_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data) { + struct dns_query *q; + SETCTXOPEN(ctx); + dns_assert_ctx(ctx); + + q = calloc(sizeof(*q), 1); + if (!q) { + ctx->dnsc_qstatus = DNS_E_NOMEM; + return NULL; + } + +#ifndef NDEBUG + q->dnsq_ctx = ctx; +#endif + q->dnsq_parse = parse; + q->dnsq_cbck = cbck ? cbck : dns_dummy_cb; + q->dnsq_cbdata = data; + + q->dnsq_origdnl0 = dns_dntodn(dn, q->dnsq_dn, sizeof(q->dnsq_dn)); + assert(q->dnsq_origdnl0 > 0); + --q->dnsq_origdnl0; /* w/o the trailing 0 */ + dns_put16(q->dnsq_typcls+0, qtyp); + dns_put16(q->dnsq_typcls+2, qcls); + q->dnsq_flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL; + + if (flags & DNS_NOSRCH || + dns_dnlabels(q->dnsq_dn) > ctx->dnsc_ndots) { + q->dnsq_nxtsrch = flags & DNS_NOSRCH ? + ctx->dnsc_srchend /* end of the search list if no search requested */ : + ctx->dnsc_srchbuf /* beginning of the list, but try as-is first */; + q->dnsq_flags |= DNS_ASIS_DONE; + dns_newid(ctx, q); + } + else { + q->dnsq_nxtsrch = ctx->dnsc_srchbuf; + dns_next_srch(ctx, q); + } + + /* q->dnsq_deadline is set to 0 (calloc above): the new query is + * "already expired" when first inserted into queue, so it's safe + * to insert it into the head of the list. Next call to dns_timeouts() + * will actually send it. + */ + qlist_add_head(&ctx->dnsc_qactive, q); + ++ctx->dnsc_nactive; + dns_request_utm(ctx, 0); + + return q; +} + +struct dns_query * +dns_submit_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse, dns_query_fn *cbck, void *data) { + int isabs; + SETCTXOPEN(ctx); + if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) { + ctx->dnsc_qstatus = DNS_E_BADQUERY; + return NULL; + } + if (isabs) + flags |= DNS_NOSRCH; + return + dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data); +} + +/* process readable fd condition. + * To be usable in edge-triggered environment, the routine + * should consume all input so it should loop over. + * Note it isn't really necessary to loop here, because + * an application may perform the loop just fine by it's own, + * but in this case we should return some sensitive result, + * to indicate when to stop calling and error conditions. + * Note also we may encounter all sorts of recvfrom() + * errors which aren't fatal, and at the same time we may + * loop forever if an error IS fatal. + */ +void dns_ioevent(struct dns_ctx *ctx, time_t now) { + int r; + unsigned servi; + struct dns_query *q; + dnsc_t *pbuf; + dnscc_t *pend, *pcur; + void *result; + union sockaddr_ns sns; + socklen_t slen; + + SETCTX(ctx); + if (!CTXOPEN(ctx)) + return; + dns_assert_ctx(ctx); + pbuf = ctx->dnsc_pbuf; + + if (!now) now = time(NULL); + +again: /* receive the reply */ + + slen = sizeof(sns); + r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf, + MSG_DONTWAIT, &sns.sa, &slen); + if (r < 0) { + /*XXX just ignore recvfrom() errors for now. + * in the future it may be possible to determine which + * query failed and requeue it. + * Note there may be various error conditions, triggered + * by both local problems and remote problems. It isn't + * quite trivial to determine whenever an error is local + * or remote. On local errors, we should stop, while + * remote errors should be ignored (for now anyway). + */ +#ifdef WINDOWS + if (WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EAGAIN) +#endif + { + dns_request_utm(ctx, now); + return; + } + goto again; + } + + pend = pbuf + r; + pcur = dns_payload(pbuf); + + /* check reply header */ + if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) { + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + + /* find the matching query, by qID */ + for (q = ctx->dnsc_qactive.head; ; q = q->dnsq_next) { + if (!q) { + /* no more requests: old reply? */ + DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); + goto again; + } + if (pbuf[DNS_H_QID1] == q->dnsq_id[0] && + pbuf[DNS_H_QID2] == q->dnsq_id[1]) + break; + } + + /* if we have numqd, compare with our query qDN */ + if (dns_numqd(pbuf)) { + /* decode the qDN */ + dnsc_t dn[DNS_MAXDN]; + if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 || + pcur + 4 > pend) { + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + if (!dns_dnequal(dn, q->dnsq_dn) || + memcmp(pcur, q->dnsq_typcls, 4) != 0) { + /* not this query */ + DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r); + goto again; + } + /* here, query match, and pcur points past qDN in query section in pbuf */ + } + /* if no numqd, we only allow FORMERR rcode */ + else if (dns_rcode(pbuf) != DNS_R_FORMERR) { + /* treat it as bad reply if !FORMERR */ + DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r); + goto again; + } + else { + /* else it's FORMERR, handled below */ + } + + /* find server */ +#ifdef HAVE_IPv6 + if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) { + for(servi = 0; servi < ctx->dnsc_nserv; ++servi) + if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6)) + break; + } + else +#endif + if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) { + for(servi = 0; servi < ctx->dnsc_nserv; ++servi) + if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin)) + break; + } + else + servi = ctx->dnsc_nserv; + + /* check if we expect reply from this server. + * Note we can receive reply from first try if we're already at next */ + if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */ + DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r); + goto again; + } + + /* we got (some) reply for our query */ + + DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r); + q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */ + + /* process the RCODE */ + switch(dns_rcode(pbuf)) { + + case DNS_R_NOERROR: + if (dns_tc(pbuf)) { + /* possible truncation. We can't deal with it. */ + /*XXX for now, treat TC bit the same as SERVFAIL. + * It is possible to: + * a) try to decode the reply - may be ANSWER section is ok; + * b) check if server understands EDNS0, and if it is, and + * answer still don't fit, end query. + */ + break; + } + if (!dns_numan(pbuf)) { /* no data of requested type */ + if (dns_next_srch(ctx, q)) { + /* if we're searching, try next searchlist element, + * but remember NODATA reply. */ + q->dnsq_flags |= DNS_SEEN_NODATA; + dns_send(ctx, q, now); + } + else + /* else - nothing to search any more - finish the query. + * It will be NODATA since we've seen a NODATA reply. */ + dns_end_query(ctx, q, DNS_E_NODATA, 0); + } + /* we've got a positive reply here */ + else if (q->dnsq_parse) { + /* if we have parsing routine, call it and return whatever it returned */ + /* don't try to re-search if NODATA here. For example, + * if we asked for A but only received CNAME. Unless we'll + * someday do recursive queries. And that's problematic too, since + * we may be dealing with specific AA-only nameservers for a given + * domain, but CNAME points elsewhere... + */ + r = q->dnsq_parse(q->dnsq_dn, pbuf, pcur, pend, &result); + dns_end_query(ctx, q, r, r < 0 ? NULL : result); + } + /* else just malloc+copy the raw DNS reply */ + else if ((result = malloc(r)) == NULL) + dns_end_query(ctx, q, DNS_E_NOMEM, NULL); + else { + memcpy(result, pbuf, r); + dns_end_query(ctx, q, r, result); + } + goto again; + + case DNS_R_NXDOMAIN: /* Non-existing domain. */ + if (dns_next_srch(ctx, q)) + /* more search entries exists, try them. */ + dns_send(ctx, q, now); + else + /* nothing to search anymore. End the query, returning either NODATA + * if we've seen it before, or NXDOMAIN if not. */ + dns_end_query(ctx, q, + q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0); + goto again; + + case DNS_R_FORMERR: + case DNS_R_NOTIMPL: + /* for FORMERR and NOTIMPL rcodes, if we tried EDNS0-enabled query, + * try w/o EDNS0. */ + if (ctx->dnsc_udpbuf > DNS_MAXPACKET && + !(q->dnsq_servnEDNS0 & (1 << servi))) { + /* we always trying EDNS0 first if enabled, and retry a given query + * if not available. Maybe it's better to remember inavailability of + * EDNS0 in ctx as a per-NS flag, and never try again for this NS. + * For long-running applications.. maybe they will change the nameserver + * while we're running? :) Also, since FORMERR is the only rcode we + * allow to be header-only, and in this case the only check we do to + * find a query it belongs to is qID (not qDN+qCLS+qTYP), it's much + * easier to spoof and to force us to perform non-EDNS0 queries only... + */ + q->dnsq_servnEDNS0 |= 1 << servi; + dns_send_this(ctx, q, servi, now); + goto again; + } + /* else we handle it the same as SERVFAIL etc */ + + case DNS_R_SERVFAIL: + case DNS_R_REFUSED: + /* for these rcodes, advance this request + * to the next server and reschedule */ + default: /* unknown rcode? hmmm... */ + break; + } + + /* here, we received unexpected reply */ + q->dnsq_servskip |= (1 << servi); /* don't retry this server */ + + /* we don't expect replies from this server anymore. + * But there may be other servers. Some may be still processing our + * query, and some may be left to try. + * We just ignore this reply and wait a bit more if some NSes haven't + * replied yet (dnsq_servwait != 0), and let the situation to be handled + * on next event processing. Timeout for this query is set correctly, + * if not taking into account the one-second difference - we can try + * next server in the same iteration sooner. + */ + + /* try next server */ + if (!q->dnsq_servwait) { + /* next retry: maybe some other servers will reply next time. + * dns_send() will end the query for us if no more servers to try. + * Note we can't continue with the next searchlist element here: + * we don't know if the current qdn exists or not, there's no definitive + * answer yet (which is seen in cases above). + *XXX standard resolver also tries as-is query in case all nameservers + * failed to process our query and if not tried before. We don't do it. + */ + dns_send(ctx, q, now); + } + else { + /* else don't do anything - not all servers replied yet */ + } + goto again; + +} + +/* handle all timeouts */ +int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) { + /* this is a hot routine */ + struct dns_query *q; + + SETCTX(ctx); + dns_assert_ctx(ctx); + + /* Pick up first entry from query list. + * If its deadline has passed, (re)send it + * (dns_send() will move it next in the list). + * If not, this is the query which determines the closest deadline. + */ + + q = ctx->dnsc_qactive.head; + if (!q) + return maxwait; + if (!now) + now = time(NULL); + do { + if (q->dnsq_deadline > now) { /* first non-expired query */ + int w = (int)(q->dnsq_deadline - now); + if (maxwait < 0 || maxwait > w) + maxwait = w; + break; + } + else { + /* process expired deadline */ + dns_send(ctx, q, now); + } + } while((q = ctx->dnsc_qactive.head) != NULL); + + dns_request_utm(ctx, now); /* update timer with new deadline */ + return maxwait; +} + +struct dns_resolve_data { + int dnsrd_done; + void *dnsrd_result; +}; + +static void dns_resolve_cb(struct dns_ctx *ctx, void *result, void *data) { + struct dns_resolve_data *d = data; + d->dnsrd_result = result; + d->dnsrd_done = 1; + ctx = ctx; +} + +void *dns_resolve(struct dns_ctx *ctx, struct dns_query *q) { + time_t now; + struct dns_resolve_data d; + int n; + SETCTXOPEN(ctx); + + if (!q) + return NULL; + + assert(ctx == q->dnsq_ctx); + dns_assert_ctx(ctx); + /* do not allow re-resolving syncronous queries */ + assert(q->dnsq_cbck != dns_resolve_cb && "can't resolve syncronous query"); + if (q->dnsq_cbck == dns_resolve_cb) { + ctx->dnsc_qstatus = DNS_E_BADQUERY; + return NULL; + } + q->dnsq_cbck = dns_resolve_cb; + q->dnsq_cbdata = &d; + d.dnsrd_done = 0; + + now = time(NULL); + while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) { +#ifdef HAVE_POLL + struct pollfd pfd; + pfd.fd = ctx->dnsc_udpsock; + pfd.events = POLLIN; + n = poll(&pfd, 1, n * 1000); +#else + fd_set rfd; + struct timeval tv; + FD_ZERO(&rfd); + FD_SET(ctx->dnsc_udpsock, &rfd); + tv.tv_sec = n; tv.tv_usec = 0; + n = select(ctx->dnsc_udpsock + 1, &rfd, NULL, NULL, &tv); +#endif + now = time(NULL); + if (n > 0) + dns_ioevent(ctx, now); + } + + return d.dnsrd_result; +} + +void *dns_resolve_dn(struct dns_ctx *ctx, + dnscc_t *dn, int qcls, int qtyp, int flags, + dns_parse_fn *parse) { + return + dns_resolve(ctx, + dns_submit_dn(ctx, dn, qcls, qtyp, flags, parse, NULL, NULL)); +} + +void *dns_resolve_p(struct dns_ctx *ctx, + const char *name, int qcls, int qtyp, int flags, + dns_parse_fn *parse) { + return + dns_resolve(ctx, + dns_submit_p(ctx, name, qcls, qtyp, flags, parse, NULL, NULL)); +} + +int dns_cancel(struct dns_ctx *ctx, struct dns_query *q) { + SETCTX(ctx); + dns_assert_ctx(ctx); + assert(q->dnsq_ctx == ctx); + /* do not allow cancelling syncronous queries */ + assert(q->dnsq_cbck != dns_resolve_cb && "can't cancel syncronous query"); + if (q->dnsq_cbck == dns_resolve_cb) + return (ctx->dnsc_qstatus = DNS_E_BADQUERY); + qlist_remove(&ctx->dnsc_qactive, q); + --ctx->dnsc_nactive; + dns_request_utm(ctx, 0); + return 0; +} + diff --git a/ami_test_client/libc-jss/udns/udns_rr_a.c b/ami_test_client/libc-jss/udns/udns_rr_a.c new file mode 100644 index 0000000000..4fdcbf9bc1 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_a.c @@ -0,0 +1,123 @@ +/* udns_rr_a.c + parse/query A/AAAA IN records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#ifndef WINDOWS +# include +# include +#endif +#include "udns.h" + +/* here, we use common routine to parse both IPv4 and IPv6 addresses. + */ + +/* this structure should match dns_rr_a[46] */ +struct dns_rr_a { + dns_rr_common(dnsa); + unsigned char *dnsa_addr; +}; + +static int +dns_parse_a(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result, unsigned dsize) { + struct dns_rr_a *ret; + struct dns_parse p; + struct dns_rr rr; + int r; + + /* first, validate and count number of addresses */ + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) + if (rr.dnsrr_dsz != dsize) + return DNS_E_PROTOCOL; + if (r < 0) + return DNS_E_PROTOCOL; + else if (!p.dnsp_nrr) + return DNS_E_NODATA; + + ret = malloc(sizeof(*ret) + dsize * p.dnsp_nrr + dns_stdrr_size(&p)); + if (!ret) + return DNS_E_NOMEM; + + ret->dnsa_nrr = p.dnsp_nrr; + ret->dnsa_addr = (unsigned char*)(ret+1); + + /* copy the RRs */ + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) + memcpy(ret->dnsa_addr + dsize * r, rr.dnsrr_dptr, dsize); + + dns_stdrr_finish((struct dns_rr_null *)ret, + (char *)(ret->dnsa_addr + dsize * p.dnsp_nrr), &p); + *result = ret; + return 0; +} + +int +dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { +#ifdef AF_INET + assert(sizeof(struct in_addr) == 4); +#endif + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A); + return dns_parse_a(qdn, pkt, cur, end, result, 4); +} + +struct dns_query * +dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a4_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_A, flags, + dns_parse_a4, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a4 * +dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_a4 *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_A, flags, dns_parse_a4); +} + +int +dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { +#ifdef AF_INET6 + assert(sizeof(struct in6_addr) == 16); +#endif + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA); + return dns_parse_a(qdn, pkt, cur, end, result, 16); +} + +struct dns_query * +dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags, + dns_query_a6_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, + dns_parse_a6, (dns_query_fn*)cbck, data); +} + +struct dns_rr_a6 * +dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_a6 *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, dns_parse_a6); +} diff --git a/ami_test_client/libc-jss/udns/udns_rr_mx.c b/ami_test_client/libc-jss/udns/udns_rr_mx.c new file mode 100644 index 0000000000..0904e01717 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_mx.c @@ -0,0 +1,91 @@ +/* udns_rr_mx.c + parse/query MX IN records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_mx(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_mx *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t mx[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_MX); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr + 2; + r = dns_getdn(pkt, &cur, end, mx, sizeof(mx)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(mx); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_mx) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnsmx_nrr = p.dnsp_nrr; + ret->dnsmx_mx = (struct dns_mx *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnsmx_mx + p.dnsp_nrr); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + ret->dnsmx_mx[r].name = sp; + cur = rr.dnsrr_dptr; + ret->dnsmx_mx[r].priority = dns_get16(cur); + cur += 2; + dns_getdn(pkt, &cur, end, mx, sizeof(mx)); + sp += dns_dntop(mx, sp, DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags, + dns_query_mx_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, + dns_parse_mx, (dns_query_fn *)cbck, data); +} + +struct dns_rr_mx * +dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_mx *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, dns_parse_mx); +} diff --git a/ami_test_client/libc-jss/udns/udns_rr_naptr.c b/ami_test_client/libc-jss/udns/udns_rr_naptr.c new file mode 100644 index 0000000000..da30069db6 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_naptr.c @@ -0,0 +1,128 @@ +/* udns_rr_naptr.c + parse/query NAPTR IN records + + Copyright (C) 2005 Michael Tokarev + Copyright (C) 2006 Mikael Magnusson + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +/* Get a single string for NAPTR record, pretty much like a DN label. + * String length is in first byte in *cur, so it can't be >255. + */ +static int dns_getstr(dnscc_t **cur, dnscc_t *ep, char *buf) +{ + unsigned l; + dnscc_t *cp = *cur; + + l = *cp++; + if (cp + l > ep) + return DNS_E_PROTOCOL; + if (buf) { + memcpy(buf, cp, l); + buf[l] = '\0'; + } + cp += l; + + *cur = cp; + return l + 1; +} + +int +dns_parse_naptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_naptr *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t dn[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_NAPTR); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + int i; + dnscc_t *ep = rr.dnsrr_dend; + + /* first 4 bytes: order & preference */ + cur = rr.dnsrr_dptr + 4; + + /* flags, services and regexp */ + for (i = 0; i < 3; i++) { + r = dns_getstr(&cur, ep, NULL); + if (r < 0) + return r; + l += r; + } + /* replacement */ + r = dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(dn); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_naptr) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnsnaptr_nrr = p.dnsp_nrr; + ret->dnsnaptr_naptr = (struct dns_naptr *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(&ret->dnsnaptr_naptr[p.dnsp_nrr]); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + cur = rr.dnsrr_dptr; + ret->dnsnaptr_naptr[r].order = dns_get16(cur); cur += 2; + ret->dnsnaptr_naptr[r].preference = dns_get16(cur); cur += 2; + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].flags = sp)); + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].service = sp)); + sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].regexp = sp)); + dns_getdn(pkt, &cur, end, dn, sizeof(dn)); + sp += dns_dntop(dn, (ret->dnsnaptr_naptr[r].replacement = sp), DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags, + dns_query_naptr_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, + dns_parse_naptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_naptr * +dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags) { + return (struct dns_rr_naptr *) + dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, dns_parse_naptr); +} diff --git a/ami_test_client/libc-jss/udns/udns_rr_ptr.c b/ami_test_client/libc-jss/udns/udns_rr_ptr.c new file mode 100644 index 0000000000..1f682aebc0 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_ptr.c @@ -0,0 +1,109 @@ +/* udns_rr_ptr.c + parse/query PTR records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include "udns.h" + +int +dns_parse_ptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_ptr *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l, c; + char *sp; + dnsc_t ptr[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_PTR); + + /* first, validate the answer and count size of the result */ + l = c = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr; + r = dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(ptr); + ++c; + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!c) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + ret = malloc(sizeof(*ret) + sizeof(char **) * c + l + dns_stdrr_size(&p)); + if (!ret) + return DNS_E_NOMEM; + ret->dnsptr_nrr = c; + ret->dnsptr_ptr = (char **)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnsptr_ptr + c); + c = 0; + dns_rewind(&p, qdn); + while((r = dns_nextrr(&p, &rr)) > 0) { + ret->dnsptr_ptr[c] = sp; + cur = rr.dnsrr_dptr; + dns_getdn(pkt, &cur, end, ptr, sizeof(ptr)); + sp += dns_dntop(ptr, sp, DNS_MAXNAME); + ++c; + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr, + dns_query_ptr_fn *cbck, void *data) { + dnsc_t dn[DNS_A4RSIZE]; + dns_a4todn(addr, 0, dn, sizeof(dn)); + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, + dns_parse_ptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_ptr * +dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr) { + return (struct dns_rr_ptr *) + dns_resolve(ctx, dns_submit_a4ptr(ctx, addr, NULL, NULL)); +} + +struct dns_query * +dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr, + dns_query_ptr_fn *cbck, void *data) { + dnsc_t dn[DNS_A6RSIZE]; + dns_a6todn(addr, 0, dn, sizeof(dn)); + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH, + dns_parse_ptr, (dns_query_fn *)cbck, data); +} + +struct dns_rr_ptr * +dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr) { + return (struct dns_rr_ptr *) + dns_resolve(ctx, dns_submit_a6ptr(ctx, addr, NULL, NULL)); +} diff --git a/ami_test_client/libc-jss/udns/udns_rr_srv.c b/ami_test_client/libc-jss/udns/udns_rr_srv.c new file mode 100644 index 0000000000..dfba4653a3 --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_srv.c @@ -0,0 +1,155 @@ +/* udns_rr_srv.c + parse/query SRV IN (rfc2782) records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + Copyright 2005 Thadeu Lima de Souza Cascardo + + 2005-09-11: + Changed MX parser file into a SRV parser file + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_srv *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + char *sp; + dnsc_t srv[DNS_MAXDN]; + + assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cur = rr.dnsrr_dptr + 6; + r = dns_getdn(pkt, &cur, end, srv, sizeof(srv)); + if (r <= 0 || cur != rr.dnsrr_dend) + return DNS_E_PROTOCOL; + l += dns_dntop_size(srv); + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnssrv_nrr = p.dnsp_nrr; + ret->dnssrv_srv = (struct dns_srv *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr); + for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) { + ret->dnssrv_srv[r].name = sp; + cur = rr.dnsrr_dptr; + ret->dnssrv_srv[r].priority = dns_get16(cur); + ret->dnssrv_srv[r].weight = dns_get16(cur+2); + ret->dnssrv_srv[r].port = dns_get16(cur+4); + cur += 6; + dns_getdn(pkt, &cur, end, srv, sizeof(srv)); + sp += dns_dntop(srv, sp, DNS_MAXNAME); + } + dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p); + *result = ret; + return 0; +} + +/* Add a single service or proto name prepending an undescore (_), + * according to rfc2782 rules. + * Return 0 or the label length. + * Routing assumes dn holds enouth space for a single DN label. */ +static int add_sname(dnsc_t *dn, const char *sn) { + int l = dns_ptodn(sn, 0, dn + 1, DNS_MAXLABEL-1, NULL); + if (l <= 1 || l - 2 != dn[1]) + /* Should we really check if sn is exactly one label? Do we care? */ + return 0; + dn[0] = l - 1; + dn[1] = '_'; + return l; +} + +/* Construct a domain name for SRV query from the given name, service and proto. + * The code allows any combinations of srv and proto (both are non-NULL, + * both NULL, or either one is non-NULL). Whenever it makes any sense or not + * is left as an exercise to programmer. + * Return negative value on error (malformed query) or addition query flag(s). + */ +static int +build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto) +{ + int p = 0, l, isabs; + if (srv) { + l = add_sname(dn + p, srv); + if (!l) + return -1; + p += l; + } + if (proto) { + l = add_sname(dn + p, proto); + if (!l) + return -1; + p += l; + } + l = dns_ptodn(name, 0, dn + p, DNS_MAXDN - p, &isabs); + if (l < 0) + return -1; + return isabs ? DNS_NOSRCH : 0; +} + +struct dns_query * +dns_submit_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, + int flags, dns_query_srv_fn *cbck, void *data) { + dnsc_t dn[DNS_MAXDN]; + int r = build_srv_dn(dn, name, srv, proto); + if (r < 0) { + dns_setstatus (ctx, DNS_E_BADQUERY); + return NULL; + } + return + dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, + dns_parse_srv, (dns_query_fn *)cbck, data); +} + +struct dns_rr_srv * +dns_resolve_srv(struct dns_ctx *ctx, + const char *name, const char *srv, const char *proto, int flags) +{ + dnsc_t dn[DNS_MAXDN]; + int r = build_srv_dn(dn, name, srv, proto); + if (r < 0) { + dns_setstatus(ctx, DNS_E_BADQUERY); + return NULL; + } + return (struct dns_rr_srv *) + dns_resolve_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, dns_parse_srv); +} diff --git a/ami_test_client/libc-jss/udns/udns_rr_txt.c b/ami_test_client/libc-jss/udns/udns_rr_txt.c new file mode 100644 index 0000000000..97f1dfb18c --- /dev/null +++ b/ami_test_client/libc-jss/udns/udns_rr_txt.c @@ -0,0 +1,98 @@ +/* udns_rr_txt.c + parse/query TXT records + + Copyright (C) 2005 Michael Tokarev + This file is part of UDNS library, an async DNS stub resolver. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library, in file named COPYING.LGPL; if not, + write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA + + */ + +#include +#include +#include +#include "udns.h" + +int +dns_parse_txt(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end, + void **result) { + struct dns_rr_txt *ret; + struct dns_parse p; + struct dns_rr rr; + int r, l; + dnsc_t *sp; + dnscc_t *cp, *ep; + + assert(dns_get16(cur+0) == DNS_T_TXT); + + /* first, validate the answer and count size of the result */ + l = 0; + dns_initparse(&p, qdn, pkt, cur, end); + while((r = dns_nextrr(&p, &rr)) > 0) { + cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; + while(cp < ep) { + r = *cp++; + if (cp + r > ep) + return DNS_E_PROTOCOL; + l += r; + cp += r; + } + } + if (r < 0) + return DNS_E_PROTOCOL; + if (!p.dnsp_nrr) + return DNS_E_NODATA; + + /* next, allocate and set up result */ + l += (sizeof(struct dns_txt) + 1) * p.dnsp_nrr + dns_stdrr_size(&p); + ret = malloc(sizeof(*ret) + l); + if (!ret) + return DNS_E_NOMEM; + ret->dnstxt_nrr = p.dnsp_nrr; + ret->dnstxt_txt = (struct dns_txt *)(ret+1); + + /* and 3rd, fill in result, finally */ + sp = (dnsc_t*)(ret->dnstxt_txt + p.dnsp_nrr); + for(dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr) > 0; ++r) { + ret->dnstxt_txt[r].txt = sp; + cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend; + while(cp < ep) { + l = *cp++; + memcpy(sp, cp, l); + sp += l; + cp += l; + } + ret->dnstxt_txt[r].len = sp - ret->dnstxt_txt[r].txt; + *sp++ = '\0'; + } + dns_stdrr_finish((struct dns_rr_null *)ret, (char*)sp, &p); + *result = ret; + return 0; +} + +struct dns_query * +dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags, + dns_query_txt_fn *cbck, void *data) { + return + dns_submit_p(ctx, name, qcls, DNS_T_TXT, flags, + dns_parse_txt, (dns_query_fn *)cbck, data); +} + +struct dns_rr_txt * +dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags) { + return (struct dns_rr_txt *) + dns_resolve_p(ctx, name, qcls, DNS_T_TXT, flags, dns_parse_txt); +} diff --git a/ami_test_client/libc-jss/utarray.h b/ami_test_client/libc-jss/utarray.h new file mode 100644 index 0000000000..4ffb630bf3 --- /dev/null +++ b/ami_test_client/libc-jss/utarray.h @@ -0,0 +1,233 @@ +/* +Copyright (c) 2008-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +/* a dynamic array implementation using macros + * see http://uthash.sourceforge.net/utarray + */ +#ifndef UTARRAY_H +#define UTARRAY_H + +#define UTARRAY_VERSION 1.9.6 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#define oom() exit(-1) + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd=*_icd; \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + a=(UT_array*)malloc(sizeof(UT_array)); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+by) > ((a)->n)) { \ + while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) + +#define utarray_insert(a,p,j) do { \ + utarray_reserve(a,1); \ + if (j > (a)->i) break; \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if (j > (a)->i) break; \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + size_t _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + size_t _ut_i; \ + if (dst->i > (size_t)(num)) { \ + if ((dst)->icd.dtor) { \ + for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ + (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ + } \ + } \ + } else if (dst->i < (size_t)(num)) { \ + utarray_reserve(dst,num-dst->i); \ + if ((dst)->icd.init) { \ + for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ + (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ + } \ + } \ + dst->i = num; \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta((dst),(src),utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < len; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ + } \ + } \ + if ((a)->i > (pos+len)) { \ + memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ + (((a)->i)-(pos+len))*((a)->icd.sz)); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new((a),(u)); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc) free(*eltc); +} +static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/ami_test_client/libc-jss/uthash-1.9.6/LICENSE b/ami_test_client/libc-jss/uthash-1.9.6/LICENSE new file mode 100755 index 0000000000..6e41e339a5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2005-2010, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/README b/ami_test_client/libc-jss/uthash-1.9.6/README new file mode 100644 index 0000000000..1c78a98939 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/README @@ -0,0 +1,41 @@ +uthash: a hash in C +============================================================================== + + +DOCUMENTATION +============= +Documentation for uthash is available in the doc/ directory or at: + + http://uthash.sourceforge.net + + +USAGE +============ +The only file you need to use uthash in your own application is + + src/uthash.h + +These bonus headers are also included: + + src/utlist.h + src/utarray.h + src/utstring.h + + +SELF-TESTS +============ +The automated self-test can be from the tests/ directory by running +make on a unix platform, or by running do_tests_win32.cmd on Windows. + + +LICENSE +============ +The BSD license applies to this software. The text is in the LICENSE file. +You can use or modify this code in your software, commercial or otherwise. + +CONTACT +============ +Feel free to send me questions, comments, ideas for improvements or bug reports. + +Troy D. Hanson, May 15, 2010 +thanson@users.sourceforge.net diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/Makefile b/ami_test_client/libc-jss/uthash-1.9.6/doc/Makefile new file mode 100644 index 0000000000..f928b552dd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/Makefile @@ -0,0 +1,43 @@ +all: css userguide changelog pdf utlist utstring utarray + +userguide: txt/userguide.txt + asciidoc --unsafe --out-file=html/userguide.html -a linkcss=1 -a theme=tdh $< + +utlist: txt/utlist.txt + asciidoc --unsafe --out-file=html/utlist.html -a linkcss=1 -a theme=tdh $< + +utarray: txt/utarray.txt + asciidoc --unsafe --out-file=html/utarray.html -a linkcss=1 -a theme=tdh $< + +utstring: txt/utstring.txt + asciidoc --unsafe --out-file=html/utstring.html -a linkcss=1 -a theme=tdh $< + +changelog: txt/ChangeLog.txt + asciidoc --out-file=html/ChangeLog.html txt/ChangeLog.txt + +stage: + cp html/userguide.html ${PAGEROOT}/uthash + cp html/userguide.pdf ${PAGEROOT}/uthash + cp html/utlist.html ${PAGEROOT}/uthash + cp html/utarray.html ${PAGEROOT}/uthash + cp html/utstring.html ${PAGEROOT}/uthash + cp html/ChangeLog.html ${PAGEROOT}/uthash + cp html/license.html ${PAGEROOT}/uthash + cp html/index.html ${PAGEROOT}/uthash + cp html/styles.css ${PAGEROOT}/uthash + +.PHONY: pdf + +pdf: txt/userguide.txt + a2x --format=pdf $< + mv txt/userguide.pdf pdf/ + cd html && ln -sf ../pdf/userguide.pdf userguide.pdf + rm -f txt/userguide.xml + +css: html/toc.css + if [ ! -d ~/.asciidoc/themes/tdh ]; then mkdir ~/.asciidoc/themes/tdh; fi + cp html/toc.css ~/.asciidoc/themes/tdh + +docbook: txt/userguide.txt + asciidoc -b docbook --out-file=/tmp/userguide.xml $< + xmlto -o html html-nochunks /tmp/userguide.xml diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/NOTES b/ami_test_client/libc-jss/uthash-1.9.6/doc/NOTES new file mode 100644 index 0000000000..da577ac3ac --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/NOTES @@ -0,0 +1,44 @@ +# maintainer notes + +# packages used: +- asciidoc +- dblatex + +# INSTALLING DBLATEX for AsciiDoc PDF generation, on Mac OS X: +-------------------------------------------------------------------------------- +# first install LaTeX if need be, as part of MacTeX (http://www.tug.org/mactex/) +# review install instructions on dblatex.sf.net +wget http://prdownloads.sourceforge.net/dblatex/dblatex-0.2.7.tar.bz2?download +tar xjf dblatex-0.2.7.tar.bz2 +cd dblatex-0.2.7 +mkdir ~/dblatex +xmlcatalog --noout --create ~/dblatex/mycatalog +# in the following command, use Spotlight or find to locate the highest revision of docbookx.dtd +# for example on CentOS it was found in 'file:///usr/share/sgml/docbook/xml-dtd-4.3-1.0-25/docbookx.dtd' +xmlcatalog --noout --add 'public' '-//OASIS//DTD DocBook? XML V4.1.2//EN' /sw/share/xml/dtd/docbookx/4.4.0/docbookx.dtd ~/dblatex/mycatalog +sudo python ./setup.py install --prefix=/usr/local --catalogs=~/dblatex/mycatalog + +# IE6 png gamma bug: +-------------------------------------------------------------------------------- +PNG images in IE6 display with wrong background colors, +solution: save PNG in Gimp *Without save gamma checked* + +# WEB SITE UPDATE +-------------------------------------------------------------------------------- +macbook: sftp thanson,uthash@web.sourceforge.net +Connecting to web.sourceforge.net... +sftp> cd htdocs +sftp> put index.html +Uploading index.html to /home/groups/u/ut/uthash/htdocs/index.html +index.html 100% 2931 2.9KB/s 00:00 + +# FILE RELEASE +-------------------------------------------------------------------------------- +macbook: sftp thanson@frs.sourceforge.net +Connecting to frs.sourceforge.net... +sftp> cd uploads +sftp> put uthash-1.4.tar.bz2 +Uploading uthash-1.4.tar.bz2 to /incoming/t/th/thanson/uploads/uthash-1.4.tar.bz2 +uthash-1.4.tar.bz2 100% 78KB 77.8KB/s 00:01 +sftp> exit + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/ChangeLog.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/ChangeLog.html new file mode 100644 index 0000000000..4eff0e133f --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/ChangeLog.html @@ -0,0 +1,1347 @@ + + + + + +uthash ChangeLog + + + + + +
+
+
+uthash at SourceForge.net +

Click to return to the uthash home page.

+
+
+
+

Version 1.9.6 (2012-04-28)

+
+
    +
  • +

    +add utarray_prev (thanks, Ben Hiett!) +

    +
  • +
  • +

    +add parens/casts for greater compatibility (thanks, Atis, Debasis Ganguly, and Steve McClellan!) +

    +
  • +
  • +

    +added ifndef to uthash_malloc and related hooks (thanks, Holger Machens!) +

    +
  • +
  • +

    +edit examples so they do not leak memory (thanks, 任晶磊!) +

    +
  • +
+
+
+
+

Version 1.9.5 (2011-11-16)

+
+
    +
  • +

    +added utarray_renew +

    +
  • +
  • +

    +fixed memory leak in uthash_clear when using Bloom filter (thanks, Jan Hättig!) +

    +
  • +
  • +

    +utarray now copies the UT_icd on array creation rather than storing a pointer +

    +
  • +
  • +

    +add parentheses to HASH_ADD to fix preprocessing of certain arguments (thanks, Aaron Rosen!) +

    +
  • +
  • +

    +more parenthesizations for greater macro argument flexibility +

    +
  • +
+
+
+
+

Version 1.9.4 (2011-06-05)

+
+
    +
  • +

    +uthash now supports MurmurHash v3 +

    +
  • +
  • +

    +utlist now includes concatenation macros (LL_CONCAT and DL_CONCAT) +

    +
  • +
  • +

    +utarray now supports binary search (utarray_find) +

    +
  • +
  • +

    +utstring now supports a new-or-clear-existing macro (utstring_renew) +

    +
  • +
  • +

    +documented technique for a multi-level hash table +

    +
  • +
  • +

    +clarified scope requirements for UT_icd in the utarray documentation +

    +
  • +
  • +

    +fixed termination when utstring_clear is followed by utstring_body +

    +
  • +
  • +

    +fixed utarray_inserta macro when used with complex arguments +

    +
  • +
  • +

    +on Visual Studio define missing type uint8_t +

    +
  • +
  • +

    +Debian/Ubuntu include uthash in the package uthash-dev. +

    +
  • +
  • +

    +uthash has been downloaded 16,211 times. +

    +
  • +
+

Thanks to Yu Feng, Richard Cook, Dino Ciuffetti, Chris Groer, and Arun Cherian +for feedback and fixes in this release!

+
+
+
+

Version 1.9.3 (2010-10-31)

+
+
    +
  • +

    +fix an ifdef for compatibility with Intel compiler (thanks, degski!) +

    +
  • +
  • +

    +fix HASH_ITER macro to satisfy C++ casting rules (thanks, Erik Bai!) +

    +
  • +
+
+
+
+

Version 1.9.2 (2010-10-04)

+
+
    +
  • +

    +new HASH_ITER macro for more convenient deletion-safe iteration +

    +
  • +
  • +

    +hashscan can now run on FreeBSD 8.1 and later (thanks, Markus Gebert!) +

    +
  • +
  • +

    +More parens to evaluate complex macro arguments properly (thanks, ngg!) +

    +
  • +
  • +

    +Add sz parameter to the uthash_free hook for platforms that do their own memory management. Hopefully this minor API change doesn’t cause too much breakage for people. (thanks, Niall Douglas!) +

    +
  • +
  • +

    +uthash has been downloaded 12,294 times +

    +
  • +
+
+
+
+

Version 1.9.1 (2010-05-15)

+
+
    +
  • +

    +Fix a redefinition warning when using uthash.h and utstring.h together +

    +
  • +
  • +

    +Fix a bug in utstring_init +

    +
  • +
  • +

    +Added HASH_FIND_PTR and HASH_ADD_PTR (thanks, Niall Douglas!) +

    +
  • +
+
+
+
+

Version 1.9 (2010-03-31)

+
+
    +
  • +

    +uthash now supports Visual Studio 2008 and 2010 in C or C++ code! +

    +
  • +
  • +

    +new headers utarray.h and utstring.h + are now included. These implement dynamic arrays and strings using macros +

    +
  • +
  • +

    +utlist.h now has deletion-safe iterators and search macros +

    +
  • +
  • +

    +the test suite now runs under Visual Studio (thanks again degski!) +

    +
  • +
  • +

    +special thanks for suggesting utarray and utlist features to Charalampos P.! +

    +
  • +
  • +

    +uthash has been downloaded 9,616 times +

    +
  • +
+
+
+
+

Version 1.8 (2009-09-08)

+
+
    +
  • +

    +Added the hashscan utility that can report on the size and quality of + hash tables in a running process (Linux-only) +

    +
  • +
  • +

    +Added Bloom filter support. This has the potential to speed up certain + types of programs that look up non-existant keys in sufficient numbers. +

    +
  • +
  • +

    +Restored the MurmurHash, which can once again be used, if an additional + symbol is defined. This is a "safety" by which the user declares they + understand that -fno-strict-aliasing flag must be used if they are + using MurmurHash under gcc with optimization. +

    +
  • +
  • +

    +Unified the bucket/table malloc hooks; now there is only one malloc hook +

    +
  • +
  • +

    +Re-organized the manual into a main section and advanced topics section +

    +
  • +
  • +

    +Fixed a bug in utlist.h where sorting a singly-linked list threw a + compile-time error. +

    +
  • +
  • +

    +Fixed a bug in utlist.h where a doubly-linked list that is sorted + did not maintain the special head->prev pointer back to the list tail. +

    +
  • +
+
+
+
+

Version 1.7 (2009-06-11)

+
+
    +
  • +

    +The MurmurHash has been removed, and Jenkin’s hash is once again the default. + While MurmurHash performed well, it’s unsafe with regard to the strict + aliasing rule. This results in incorrect code when compiled with optimization. + It’s not possible to enable -fno-strict-aliasing from within a header file. +

    +
  • +
  • +

    +The linked list macros in utlist.h now comply with the strict-aliasing + rule so they generate correct code under high optimization levels (O2 or O3). + The use of the __typeof__ extension, which was originally a GNU extension, + may reduce portability to other compilers that do not support this extension. + This extension is used in the singly-linked list macros and the sort macros. +

    +
  • +
+
+
+
+

Version 1.6 (2009-05-08)

+
+

Special thanks to Alfred Heisner for contributing several enhancements:

+
    +
  • +

    +Support for two new hash functions: +

    +
      +
    • +

      +the Paul Hsieh hash function (HASH_SFH) +

      +
    • +
    • +

      +Austin Appleby’s MurmurHash function (HASH_MUR) +

      +
    • +
    +
  • +
  • +

    +Because of its excellent performance, MurmurHash is now the default hash function. +

    +
  • +
  • +

    +keystats now has much better elapsed time accuracy under Cygwin and MinGW +

    +
  • +
  • +

    +fixed casting in HASH_FNV, HASH_SAX and HASH_OAT for non-char keys +

    +
  • +
+

This release also includes:

+
    +
  • +

    +a new HASH_CLEAR operation clears a hash table in one step. +

    +
  • +
  • +

    +a new HASH_SELECT operation inserts those elements from one hash that + satisfy a given condition into another hash. The selected items have + dual presence in both hash tables. For example a game could select the + visible polygons from a hash of all polygons. +

    +
  • +
  • +

    +fixed a compile-time error which occurred if the final argument to + HASH_ADD_KEYPTR was a pointer to an array member like &a[i] +

    +
  • +
  • +

    +added another test script tests/all_funcs which executes the test suite + using every supported hash function +

    +
  • +
+

And lastly,

+
    +
  • +

    +a new, separate header called utlist.h is included which + provides linked list macros for C structures, similar in style to the + uthash macros +

    +
  • +
+
+
+
+

Version 1.5 (2009-02-19)

+
+
    +
  • +

    +now thread-safe for concurrent readers +

    +
  • +
  • +

    +use scratch variables on stack rather than in table (thanks, Petter Arvidsson!). + This change made HASH_FIND about 13% faster and enabled reader concurrency. +

    +
  • +
  • +

    +made BSD license terms even more permissive +

    +
  • +
  • +

    +added PDF version of User Guide +

    +
  • +
  • +

    +added update news +(RSS) + +

    +
  • +
+
+
+
+

Version 1.4 (2008-09-23)

+
+
    +
  • +

    +Add HASH_COUNT for counting items in the hash +

    +
  • +
  • +

    +Compatibility with C++. Satisfy additional casting requirements. + Also in the tests/ directory, running make cplusplus now compiles + all the test programs with the C++ compiler. +

    +
  • +
  • +

    +Eliminate elmt pointer from the UT_hash_handle. Calculate elmt + from hash handle address by subtracting hho (hash handle offset). +

    +
  • +
  • +

    +Contributed by L.S.Chin: + Cast void* to char* before pointer arithmetic to suppress compiler + warnings. We assume compilers abide to C standards which impose + requirement that sizeof(void*) == sizeof(char*). +

    +
  • +
  • +

    +Return meaningful exit status from do_tests per Tiago Cunha, + so that package manager-based install can verify tests are successful +

    +
  • +
+
+
+
+

Version 1.3 (2008-07-27)

+
+
    +
  • +

    +use integer-only math-- no floating point! Support FPU-less CPU’s. +

    +
  • +
  • +

    +eliminate hash_q metric, which measured the fraction of items with + non-ideal chain positions. We only need to know if this fraction + is below 0.5. This is now determined using fast bitwise tests. +

    +
  • +
  • +

    +when an item is added to the hash, calculate the key’s hash value + upfront and store it, instead of recomputing it as needed. This hashv + is stored in the hash handle. Potentially major speed benefit for + bucket expansion algorithm. Deleting is marginally improved too. +

    +
  • +
  • +

    +fixed a minor bug in the calculation of the max ideal chain length; + line 446 in v1.2 erroneously calculated a/b*2 instead of a/(b*2). + The effect of this bug was that bucket expansion could occur more + readily because the per-bucket max chain length multiplier factor + (which delays bucket expansion when certain buckets are overused) + was set to a lower, expansion-favoring value than intended. +

    +
  • +
  • +

    +improved source commenting and improved variable names in structures +

    +
  • +
  • +

    +remove HASH_JSW. Lengthy random number array made code less readable +

    +
  • +
  • +

    +add HASH_SRT(hh,hash,cmp) as a generalized HASH_SORT(hash,cmp). + It was an omission in uthash 1.2 that there was no sort macro for + hash handles with names other than hh. +

    +
  • +
  • +

    +Corrected HASH_FSCK so it works with any name for the hash handle. +

    +
  • +
  • +

    +behave properly in pathological HASH_DEL(a,a) case where the same + variable references the head and the deletee (advancing the head + then loses the correct reference to the deletee); fix by using + scratch area in the hash table to store deletee hash handle. +

    +
  • +
  • +

    +made tests runnable on MinGW +

    +
  • +
  • +

    +3000+ downloads since uthash-1.0 +

    +
  • +
+
+
+
+

Version 1.2 (2006-11-22)

+
+
    +
  • +

    +new HASH_SORT macro +

    +
  • +
  • +

    +Cygwin support +

    +
  • +
  • +

    +User Guide now features a clickable Table of Contents. + (The technique for generating the TOC on the browser was contributed + back to the AsciiDoc project and incorporated into AsciiDoc v8.1.0). +

    +
  • +
+
+
+
+

Version 1.1 (2006-06-28)

+
+
    +
  • +

    +uthash-1.1 released +

    +
  • +
  • +

    +supports several built-in user-selectable hash functions +

    +
  • +
  • +

    +new keystats utility quantifies performance of hash functions +

    +
  • +
+
+
+
+

Version 1.0 (2006-06-02)

+
+
    +
  • +

    +Initial release +

    +
  • +
+
+
+
+

+ + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/banner.png b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..de4f310b9fec8cfda8f394e26cc7850968e1c673 GIT binary patch literal 20477 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ut hash + + + + + + + + + + + + + + + + + + + + + + + a hash tablefor C structures + + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.png b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..8fab0faf96921882cf2d9507c990aa06820859a3 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^tAUt{gAGWYUa<}&z?S6g?!xdN1Q+aGJ{c&&S>O>_ z%)r1c48n{Iv*t(u1=&kHeO=kFv9XAVaNRx1)C3e-@9E+g5^?zLq=Q_C3YfWGphgw(XJUjB?4cO}jZ0tt?_27`8Z>*570Gc>PWD`{B#s4ySf) z32(acBX!;7-@EtUH2liOsuK6tDE@wI$=SHfFLr13e_ry@zr2rS_OdXC`&UA015NH< z53fD-{Pr@vU8kj{zT3t$X4oH0H04`u$hW#Mq8EY=XZ0M; dI+K5g@u&WCk=0-J%m6x_!PC{xWt~$(69DlBcvS!Z literal 0 HcmV?d00001 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.svg b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.svg new file mode 100644 index 0000000000..8ea0e0913a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/grad_blue.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/rss.png b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/rss.png new file mode 100755 index 0000000000000000000000000000000000000000..b3c949d2244f2c0c81d65e74719af2a1b56d06a3 GIT binary patch literal 689 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 literal 0 HcmV?d00001 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash-mini.svg b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash-mini.svg new file mode 100644 index 0000000000..ea2d0749db --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash-mini.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ut hash + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash.png b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/img/uthash.png new file mode 100644 index 0000000000000000000000000000000000000000..20df5a7d3566fa1210102c6512956916d30d43a5 GIT binary patch literal 21518 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�L6@iTtUNOob*ju%^I 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 FV1mJ@2TK3| literal 0 HcmV?d00001 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/index.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/index.html new file mode 100644 index 0000000000..15426e82b7 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/index.html @@ -0,0 +1,134 @@ + + + + + uthash: a hash table for C structures + + + + + +
+ sf.net summary page > + uthash home +
+ +
+
+ + + + +
+ Any C structure can be stored in a hash table using uthash. Just add a + UT_hash_handle to the structure and choose one or more fields + in your structure to act as the key. Then use these macros to store, + retrieve or delete items from the hash table. + +
+Example 1. Adding an item to a hash. +
+
+#include "uthash.h"
+
+struct my_struct {
+    int id;            /* we'll use this field as the key */
+    char name[10];             
+    UT_hash_handle hh; /* makes this structure hashable */
+};
+
+struct my_struct *users = NULL;
+
+void add_user(struct my_struct *s) {
+    HASH_ADD_INT( users, id, s );    
+}
+
+
+
+
+ +
+Example 2. Looking up an item in a hash. +
+
+struct my_struct *find_user(int user_id) {
+    struct my_struct *s;
+
+    HASH_FIND_INT( users, &user_id, s );  
+    return s;
+}
+
+
+
+
+ +
+Example 3. Deleting an item from a hash. +
+ +
+void delete_user(struct my_struct *user) {
+    HASH_DEL( users, user);  
+}
+
+
+
+
+ + For more information and examples, please see the User Guide. + +
+
+ +
+ + + + + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/license.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/license.html new file mode 100644 index 0000000000..c5f1a74793 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/license.html @@ -0,0 +1,59 @@ + + + + + uthash: a hash table for C structures + + + + + +
+ sf.net summary page > + uthash home > + BSD license +
+ +
+
+
+
+Copyright (c) 2005-2011, Troy D. Hanson    http://uthash.sourceforge.net
+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.
+
+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.
+
+
+
+ +
+ + + + + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/styles.css b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/styles.css new file mode 100644 index 0000000000..75035f16d8 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/styles.css @@ -0,0 +1,130 @@ +#banner { + /* font-size: x-large; */ + /* background: #ff00ff; */ + /* height: 100px; */ +} + +#topnav { + /* background-image: url(img/grad_topnav.png); */ + /* background-repeat: repeat-y; */ + /* background-color: #af00af; */ + /* height: 25px; */ + margin-top: 5px; + margin-bottom: 10px; + padding: 3px; + font-size: 9pt; + font-family: sans-serif; + /* border-style: solid; */ + /* border-width: 1px; */ +} + +#topnav a { + padding: 8px; +} + +h1,p { margin: 0; } /* non-0 margin on firefox */ + +#mid { + background-image: url(img/grad_blue.png); + background-repeat: repeat-y; + /* background-color: #ffddaa; */ + padding-top: 20px; + margin-bottom: 10px; +} + +#mid img { + padding-left: 10px; + vertical-align: middle; +} + +a img { + border: 0 +} + +#nav { + background-color: #fff8f1; + margin-left: 10px; + margin-top: 20px; + float: left; + padding: 10px; + border-style: solid; + border-width: 2px; + font-family: sans-serif; +} + + +#nav h2 { + font-weight: bold; + font-size: 10pt; +} + +#nav h3 { + /* font-weight: bold; */ + padding-left: 5px; + /* font-style: oblique; */ + font-family: sans-serif; + font-size: 7pt; +} + +#nav div { + font-size: 9pt; + padding-left: 15px; +} + +#main { + background: #ffffff; + margin-top: 20px; + margin-left: 170px; + padding-left: 20px; + height: 100%; +} + +#main h1 { + font-family: sans-serif; +} + +.listing { + margin: 20px; + font-family: sans-serif; + font-weight: bold; +} + +.code { + padding: 10px; + font-size: 8pt; + font-weight: normal; + background: #f3f3f3; + width: 80%; + border-style: solid; + border-width: 1px; +} + +#footer { + /* background: #00ffff; */ + margin-top: 5px; + font-size: small; + font-family: sans-serif; +} + +hr { + height: 0.04em; + background: black; + margin: 0 10% 0 0; +} + +#footer { + width: 90%; +} + +#footer img { + margin-right: 5px; + float: right; +} + +#footer #support { + float: right; +} + +body { + width: 80%; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh-quirks.css b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh-quirks.css new file mode 100644 index 0000000000..a39a6d65a6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh-quirks.css @@ -0,0 +1,41 @@ +/* Workarounds for IE6's broken and incomplete CSS2. */ + +div.sidebar-content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} +div.sidebar-title, div.image-title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + margin-top: 0.0em; + margin-bottom: 0.5em; +} + +div.listingblock div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock-attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock-content { + white-space: pre; +} +div.verseblock-attribution { + padding-top: 0.75em; + text-align: left; +} + +div.exampleblock-content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +/* IE6 sets dynamically generated links as visited. */ +div#toc a:visited { color: blue; } diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh.css b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh.css new file mode 100644 index 0000000000..4f927f3d77 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/tdh.css @@ -0,0 +1,402 @@ +/* Debug borders */ +p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 { +/* + border: 1px solid red; +*/ +} + +body { + margin: 1em 5% 1em 5%; +} + +a { + color: blue; + text-decoration: underline; +} +a:visited { + color: fuchsia; +} + +em { + font-style: italic; + color: navy; +} + +strong { + font-weight: bold; + color: #083194; +} + +tt { + color: navy; +} + +h1, h2, h3, h4, h5, h6 { + color: #527bbd; + font-family: sans-serif; + margin-top: 1.2em; + margin-bottom: 0.5em; + line-height: 1.3; +} + +h1, h2, h3 { + border-bottom: 2px solid silver; +} +h2 { + padding-top: 0.5em; +} +h3 { + float: left; +} +h3 + * { + clear: left; +} + +div.sectionbody { + font-family: serif; + margin-left: 0; +} + +hr { + border: 1px solid silver; +} + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +ul, ol, li > p { + margin-top: 0; +} + +pre { + padding: 0; + margin: 0; +} + +span#author { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + font-size: 1.1em; +} +span#email { +} +span#revnumber, span#revdate, span#revremark { + font-family: sans-serif; +} + +div#footer { + font-family: sans-serif; + font-size: small; + border-top: 2px solid silver; + padding-top: 0.5em; + margin-top: 4.0em; +} +div#footer-text { + float: left; + padding-bottom: 0.5em; +} +div#footer-badges { + float: right; + padding-bottom: 0.5em; +} + +div#preamble { + margin-top: 1.5em; + margin-bottom: 1.5em; +} +div.tableblock, div.imageblock, div.exampleblock, div.verseblock, +div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, +div.admonitionblock { + margin-top: 1.0em; + margin-bottom: 1.5em; +} +div.admonitionblock { + margin-top: 2.0em; + margin-bottom: 2.0em; + margin-right: 10%; + color: #606060; +} + +div.content { /* Block element content. */ + padding: 0; +} + +/* Block element titles. */ +div.title, caption.title { + color: #527bbd; + font-family: sans-serif; + font-weight: bold; + text-align: left; + margin-top: 1.0em; + margin-bottom: 0.5em; +} +div.title + * { + margin-top: 0; +} + +td div.title:first-child { + margin-top: 0.0em; +} +div.content div.title:first-child { + margin-top: 0.0em; +} +div.content + div.title { + margin-top: 0.0em; +} + +div.sidebarblock > div.content { + background: #ffffee; + border: 1px solid silver; + padding: 0.5em; +} + +div.listingblock > div.content { + border: 1px solid silver; + background: #f4f4f4; + padding: 0.5em; +} + +div.quoteblock, div.verseblock { + padding-left: 1.0em; + margin-left: 1.0em; + margin-right: 10%; + border-left: 5px solid #dddddd; + color: #777777; +} + +div.quoteblock > div.attribution { + padding-top: 0.5em; + text-align: right; +} + +div.verseblock > div.content { + white-space: pre; +} +div.verseblock > div.attribution { + padding-top: 0.75em; + text-align: left; +} +/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ +div.verseblock + div.attribution { + text-align: left; +} + +div.admonitionblock .icon { + vertical-align: top; + font-size: 1.1em; + font-weight: bold; + text-decoration: underline; + color: #527bbd; + padding-right: 0.5em; +} +div.admonitionblock td.content { + padding-left: 0.5em; + border-left: 3px solid #dddddd; +} + +div.exampleblock > div.content { + border-left: 3px solid #dddddd; + padding-left: 0.5em; +} + +div.imageblock div.content { padding-left: 0; } +span.image img { border-style: none; } +a.image:visited { color: white; } + +dl { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +dt { + margin-top: 0.5em; + margin-bottom: 0; + font-style: normal; + color: navy; +} +dd > *:first-child { + margin-top: 0.1em; +} + +ul, ol { + list-style-position: outside; +} +ol.arabic { + list-style-type: decimal; +} +ol.loweralpha { + list-style-type: lower-alpha; +} +ol.upperalpha { + list-style-type: upper-alpha; +} +ol.lowerroman { + list-style-type: lower-roman; +} +ol.upperroman { + list-style-type: upper-roman; +} + +div.compact ul, div.compact ol, +div.compact p, div.compact p, +div.compact div, div.compact div { + margin-top: 0.1em; + margin-bottom: 0.1em; +} + +div.tableblock > table { + border: 3px solid #527bbd; +} +thead, p.table.header { + font-family: sans-serif; + font-weight: bold; +} +tfoot { + font-weight: bold; +} +td > div.verse { + white-space: pre; +} +p.table { + margin-top: 0; +} +/* Because the table frame attribute is overriden by CSS in most browsers. */ +div.tableblock > table[frame="void"] { + border-style: none; +} +div.tableblock > table[frame="hsides"] { + border-left-style: none; + border-right-style: none; +} +div.tableblock > table[frame="vsides"] { + border-top-style: none; + border-bottom-style: none; +} + + +div.hdlist { + margin-top: 0.8em; + margin-bottom: 0.8em; +} +div.hdlist tr { + padding-bottom: 15px; +} +dt.hdlist1.strong, td.hdlist1.strong { + font-weight: bold; +} +td.hdlist1 { + vertical-align: top; + font-style: normal; + padding-right: 0.8em; + color: navy; +} +td.hdlist2 { + vertical-align: top; +} +div.hdlist.compact tr { + margin: 0; + padding-bottom: 0; +} + +.comment { + background: yellow; +} + +.footnote, .footnoteref { + font-size: 0.8em; +} + +span.footnote, span.footnoteref { + vertical-align: super; +} + +#footnotes { + margin: 20px 0 20px 0; + padding: 7px 0 0 0; +} + +#footnotes div.footnote { + margin: 0 0 5px 0; +} + +#footnotes hr { + border: none; + border-top: 1px solid silver; + height: 1px; + text-align: left; + margin-left: 0; + width: 20%; + min-width: 100px; +} + + +@media print { + div#footer-badges { display: none; } +} + +div#toc { + margin-bottom: 2.5em; +} + +div#toctitle { + color: #527bbd; + font-family: sans-serif; + font-size: 1.1em; + font-weight: bold; + margin-top: 1.0em; + margin-bottom: 0.1em; +} + +div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { + margin-top: 0; + margin-bottom: 0; +} +div.toclevel2 { + margin-left: 2em; + font-size: 0.9em; +} +div.toclevel3 { + margin-left: 4em; + font-size: 0.9em; +} +div.toclevel4 { + margin-left: 6em; + font-size: 0.9em; +} +#toc { + float: right; + font-family: sans-serif; + border: 1px solid #000; + margin: 0px 0px 20px 20px; + padding: 0px; + background: #f0f0f0; + font-size: 80%; +} + +#toc #hdr { + color:#ffffff; + background:#98b1c4; + text-align:center; + margin-bottom:5px; +} + +a img { + border: 0 +} + +#toc a:visited, #toc a:link { color:#000; text-decoration: none } +#toc a:hover { color:#00f; text-decoration: underline; } + +#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; } +#toc .level3 { margin-left: 2em; font-size: 0.8em } + +.toplink { + float: right; + font-size: 50%; + cursor: pointer; +} + +#topnav {font-weight: bold} +#topnav a {font-weight: normal} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/toc.css b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/toc.css new file mode 100644 index 0000000000..ae3363ee51 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/toc.css @@ -0,0 +1,35 @@ +#toc { + float: right; + font-family: sans-serif; + border: 1px solid #000; + margin: 0px 0px 20px 20px; + padding: 0px; + background: #f0f0f0; + font-size: 80%; +} + +#toc #hdr { + color:#ffffff; + background:#98b1c4; + text-align:center; + margin-bottom:5px; +} + +a img { + border: 0 +} + +#toc a:visited, #toc a:link { color:#000; text-decoration: none } +#toc a:hover { color:#00f; text-decoration: underline; } + +#toc .level2 { margin-left: 1em; margin-top: 2px; margin-bottom: 2px; text-decoration: underline; } +#toc .level3 { margin-left: 2em; font-size: 0.8em } + +.toplink { + float: right; + font-size: 50%; + cursor: pointer; +} + +#topnav {font-weight: bold} +#topnav a {font-weight: normal} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.html new file mode 100644 index 0000000000..515859a738 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.html @@ -0,0 +1,2240 @@ + + + + + +uthash User Guide + + + + + + + +
+
+
+uthash at SourceForge.net +
+ sf.net summary page + >> uthash home + >> User Guide + [View PDF] +
+
+
+
+

A hash in C

+
+
+ +

This document is written for C programmers. Since you’re reading this, chances +are that you know a hash is used for looking up items using a key. In scripting +languages like Perl, hashes are used all the time. In C, hashes don’t exist in +the language itself. This software provides a hash table for C structures.

+
+

What can it do?

+

This software supports these operations on items in a hash table:

+
    +
  1. +

    +add +

    +
  2. +
  3. +

    +find +

    +
  4. +
  5. +

    +delete +

    +
  6. +
  7. +

    +count +

    +
  8. +
  9. +

    +iterate +

    +
  10. +
  11. +

    +sort +

    +
  12. +
  13. +

    +select +

    +
  14. +
+
+
+

Is it fast?

+

Add, find and delete are normally constant-time operations. This is influenced +by your key domain and the hash function.

+

This hash aims to be minimalistic and efficient. It’s around 900 lines of C. +It inlines automatically because it’s implemented as macros. It’s fast as long +as the hash function is suited to your keys. You can use the default hash +function, or easily compare performance and choose from among several other +built-in hash functions.

+
+
+

Is it a library?

+

No, it’s just a single header file: uthash.h. All you need to do is copy +the header file into your project, and:

+
+
+
#include "uthash.h"
+
+

Since uthash is a header file only, there is no library code to link against.

+
+
+

C/C++ and platforms

+

This software can be used in C and C++ programs. It has been tested on:

+
    +
  • +

    +Linux +

    +
  • +
  • +

    +Mac OS X +

    +
  • +
  • +

    +Windows using Visual Studio 2008 and 2010 +

    +
  • +
  • +

    +Solaris +

    +
  • +
  • +

    +OpenBSD +

    +
  • +
  • +

    +FreeBSD +

    +
  • +
+
+

Test suite

+

To run the test suite, enter the tests directory. Then,

+
    +
  • +

    +on Unix platforms, run make +

    +
  • +
  • +

    +on Windows, run the "do_tests_win32.cmd" batch file. (You may edit the + batch file if your Visual Studio is installed in a non-standard location). +

    +
  • +
+
+
+
+

BSD licensed

+

This software is made available under the +revised BSD license. +It is free and open source.

+
+
+

Obtaining uthash

+

Please follow the link to download on the +uthash website at http://uthash.sourceforge.net.

+

A number of platforms include uthash in their package repositories. For example, +Debian/Ubuntu users may simply aptitude install uthash-dev.

+
+
+

Getting help

+ +
+
+

Resources

+

Users of uthash may wish to follow the news feed for information about new +releases. Also, there are some extra bonus headers included with uthash.

+
+
+News +
+
+

+ The author has a news feed for software updates +(RSS) +. +

+
+
+Extras included with uthash +
+
+

+ uthash ships with these "extras"-- independent headers similar to uthash. + First utlist.h provides linked list macros for C structures. + Second, utarray.h implements dynamic arrays using macros. + Third, utstring.h implements a basic dynamic string. +

+
+
+Other software +
+
+

+ Other open-source software by the author is listed at http://tkhanson.net. +

+
+
+
+
+

Who’s using it?

+

Since releasing uthash in 2006, it has been downloaded thousands of times, +incorporated into commercial software, academic research, and into other +open-source software.

+
+
+
+
+

Your structure

+
+

In uthash, a hash table is comprised of structures. Each structure represents a +key-value association. One or more of the structure fields constitute the key. +The structure pointer itself is the value.

+
+
Defining a structure that can be hashed
+
+
#include "uthash.h"
+
+struct my_struct {
+    int id;                    /* key */
+    char name[10];
+    UT_hash_handle hh;         /* makes this structure hashable */
+};
+
+

Note that, in uthash, your structure will never be moved or copied into another +location when you add it into a hash table. This means that you can keep other +data structures that safely point to your structure-- regardless of whether you +add or delete it from a hash table during your program’s lifetime.

+
+

The key

+

There are no restrictions on the data type or name of the key field. The key +can also comprise multiple contiguous fields, having any names and data types.

+
+
+
Any data type… really?
+

Yes, your key and structure can have any data type. Unlike function calls with +fixed prototypes, uthash consists of macros-- whose arguments are untyped-- and +thus able to work with any type of structure or key.

+
+
+

Unique keys

+

As with any hash, every item must have a unique key. Your application must +enforce key uniqueness. Before you add an item to the hash table, you must +first know (if in doubt, check!) that the key is not already in use. You +can check whether a key already exists in the hash table using HASH_FIND.

+
+
+
+

The hash handle

+

The UT_hash_handle field must be present in your structure. It is used for +the internal bookkeeping that makes the hash work. It does not require +initialization. It can be named anything, but you can simplify matters by +naming it hh. This allows you to use the easier "convenience" macros to add, +find and delete items.

+
+
+

A word about memory

+

Some have asked how uthash cleans up its internal memory. The answer is simple: +when you delete the final item from a hash table, uthash releases all the +internal memory associated with that hash table, and sets its pointer to NULL.

+
+
+
+
+

Hash operations

+
+

This section introduces the uthash macros by example. For a more succinct +listing, see Macro Reference.

+
+
+
Convenience vs. general macros:
+

The uthash macros fall into two categories. The convenience macros can be used +with integer, pointer or string keys (and require that you chose the conventional +name hh for the UT_hash_handle field). The convenience macros take fewer +arguments than the general macros, making their usage a bit simpler for these +common types of keys.

+

The general macros can be used for any types of keys, or for multi-field keys, +or when the UT_hash_handle has been named something other than hh. These +macros take more arguments and offer greater flexibility in return. But if the +convenience macros suit your needs, use them-- your code will be more readable.

+
+
+

Declare the hash

+

Your hash must be declared as a NULL-initialized pointer to your structure.

+
+
+
struct my_struct *users = NULL;    /* important! initialize to NULL */
+
+
+
+

Add item

+

Allocate and initialize your structure as you see fit. The only aspect +of this that matters to uthash is that your key must be initialized to +a unique value. Then call HASH_ADD. (Here we use the convenience macro +HASH_ADD_INT, which offers simplified usage for keys of type int).

+
+
Add an item to a hash
+
+
void add_user(int user_id, char *name) {
+    struct my_struct *s;
+
+    s = malloc(sizeof(struct my_struct));
+    s->id = user_id;
+    strcpy(s->name, name);
+    HASH_ADD_INT( users, id, s );  /* id: name of key field */
+}
+
+

The first parameter to HASH_ADD_INT is the hash table, and the +second parameter is the name of the key field. Here, this is id. The +last parameter is a pointer to the structure being added.

+
+
+
Wait.. the field name is a parameter?
+

If you find it strange that id, which is the name of a field in the +structure, can be passed as a parameter, welcome to the world of macros. Don’t +worry- the C preprocessor expands this to valid C code.

+
+
+

Key must not be modified while in-use

+

Once a structure has been added to the hash, do not change the value of its key. +Instead, delete the item from the hash, change the key, and then re-add it.

+
+
+

Passing the hash pointer into functions

+

In the example above users is a global variable, but what if the caller wanted +to pass the hash pointer into the add_user function? At first glance it would +appear that you could simply pass users as an argument, but that won’t work +right.

+
+
+
/* bad */
+void add_user(struct my_struct *users, int user_id, char *name) {
+  ...
+  HASH_ADD_INT( users, id, s );
+}
+
+

You really need to pass a pointer to the hash pointer:

+
+
+
/* good */
+void add_user(struct my_struct **users, int user_id, char *name) { ...
+  ...
+  HASH_ADD_INT( *users, id, s );
+}
+
+

Note that we dereferenced the pointer in the HASH_ADD also.

+

The reason it’s necessary to deal with a pointer to the hash pointer is simple: +the hash macros modify it (in other words, they modify the pointer itself not +just what it points to).

+
+
+
+

Find item

+

To look up a structure in a hash, you need its key. Then call HASH_FIND. +(Here we use the convenience macro HASH_FIND_INT for keys of type int).

+
+
Find a structure using its key
+
+
struct my_struct *find_user(int user_id) {
+    struct my_struct *s;
+
+    HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */
+    return s;
+}
+
+

Here, the hash table is users, and &user_id points to the key (an integer +in this case). Last, s is the output variable of HASH_FIND_INT. The +final result is that s points to the structure with the given key, or +is NULL if the key wasn’t found in the hash.

+
+ + + +
+
Note
+
The middle argument is a pointer to the key. You can’t pass a literal key +value to HASH_FIND. Instead assign the literal value to a variable, and pass +a pointer to the variable.
+
+
+
+

Delete item

+

To delete a structure from a hash, you must have a pointer to it. (If you only +have the key, first do a HASH_FIND to get the structure pointer).

+
+
Delete an item from a hash
+
+
void delete_user(struct my_struct *user) {
+    HASH_DEL( users, user);  /* user: pointer to deletee */
+    free(user);              /* optional; it's up to you! */
+}
+
+

Here again, users is the hash table, and user is a pointer to the +structure we want to remove from the hash.

+
+

uthash never frees your structure

+

Deleting a structure just removes it from the hash table-- it doesn’t free +it. The choice of when to free your structure is entirely up to you; uthash +will never free your structure.

+
+
+

Delete can change the pointer

+

The hash table pointer (which initially points to the first item added to the +hash) can change in response to HASH_DEL (i.e. if you delete the first item +in the hash table).

+
+
+

Iterative deletion

+

The HASH_ITER macro is a deletion-safe iteration construct which expands +to a simple for loop.

+
+
Delete all items from a hash
+
+
void delete_all() {
+  struct my_struct *current_user, *tmp;
+
+  HASH_ITER(hh, users, current_user, tmp) {
+    HASH_DEL(users,current_user);  /* delete; users advances to next */
+    free(current_user);            /* optional- if you want to free  */
+  }
+}
+
+
+
+

All-at-once deletion

+

If you only want to delete all the items, but not free them or do any +per-element clean up, you can do this more efficiently in a single operation:

+
+
+
HASH_CLEAR(hh,users);
+
+

Afterward, the list head (here, users) will be set to NULL.

+
+
+
+

Count items

+

The number of items in the hash table can be obtained using HASH_COUNT:

+
+
Count of items in the hash table
+
+
unsigned int num_users;
+num_users = HASH_COUNT(users);
+printf("there are %u users\n", num_users);
+
+

Incidentally, this works even the list (users, here) is NULL, in +which case the count is 0.

+
+
+

Iterating and sorting

+

You can loop over the items in the hash by starting from the beginning and +following the hh.next pointer.

+
+
Iterating over all the items in a hash
+
+
void print_users() {
+    struct my_struct *s;
+
+    for(s=users; s != NULL; s=s->hh.next) {
+        printf("user id %d: name %s\n", s->id, s->name);
+    }
+}
+
+

There is also an hh.prev pointer you could use to iterate backwards through +the hash, starting from any known item.

+
+

Deletion-safe iteration

+

In the example above, it would not be safe to delete and free s in the body +of the for loop, (because s is derefenced each time the loop iterates). +This is easy to rewrite correctly (by copying the s->hh.next pointer to a +temporary variable before freeing s), but it comes up often enough that a +deletion-safe iteration macro, HASH_ITER, is included. It expands to a +for-loop header. Here is how it could be used to rewrite the last example:

+
+
+
struct my_struct *s, *tmp;
+
+
+
+
HASH_ITER(hh, users, s, tmp) {
+    printf("user id %d: name %s\n", s->id, s->name);
+    /* ... it is safe to delete and free s here */
+}
+
+
+
+
A hash is also a doubly-linked list.
+

Iterating backward and forward through the items in the hash is possible +because of the hh.prev and hh.next fields. All the items in the hash can +be reached by repeatedly following these pointers, thus the hash is also a +doubly-linked list.

+
+

If you’re using uthash in a C++ program, you need an extra cast on the for +iterator, e.g., s=(struct my_struct*)s->hh.next.

+
+
+

Sorted iteration

+

The items in the hash are, by default, traversed in the order they were added +("insertion order") when you follow the hh.next pointer. But you can sort +the items into a new order using HASH_SORT. E.g.,

+
+
+
HASH_SORT( users, name_sort );
+
+

The second argument is a pointer to a comparison function. It must accept two +arguments which are pointers to two items to compare. Its return value should +be less than zero, zero, or greater than zero, if the first item sorts before, +equal to, or after the second item, respectively. (Just like strcmp).

+
+
Sorting the items in the hash
+
+
int name_sort(struct my_struct *a, struct my_struct *b) {
+    return strcmp(a->name,b->name);
+}
+
+int id_sort(struct my_struct *a, struct my_struct *b) {
+    return (a->id - b->id);
+}
+
+void sort_by_name() {
+    HASH_SORT(users, name_sort);
+}
+
+void sort_by_id() {
+    HASH_SORT(users, id_sort);
+}
+
+

When the items in the hash are sorted, the first item may change position. In +the example above, users may point to a different structure after calling +HASH_SORT.

+
+
+
+

A complete example

+

We’ll repeat all the code and embellish it with a main() function to form a +working example.

+

If this code was placed in a file called example.c in the same directory as +uthash.h, it could be compiled and run like this:

+
+
+
cc -o example example.c
+./example
+
+

Follow the prompts to try the program.

+
+
A complete program
+
+
#include <stdio.h>   /* gets */
+#include <stdlib.h>  /* atoi, malloc */
+#include <string.h>  /* strcpy */
+#include "uthash.h"
+
+struct my_struct {
+    int id;                    /* key */
+    char name[10];
+    UT_hash_handle hh;         /* makes this structure hashable */
+};
+
+struct my_struct *users = NULL;
+
+void add_user(int user_id, char *name) {
+    struct my_struct *s;
+
+    s = (struct my_struct*)malloc(sizeof(struct my_struct));
+    s->id = user_id;
+    strcpy(s->name, name);
+    HASH_ADD_INT( users, id, s );  /* id: name of key field */
+}
+
+struct my_struct *find_user(int user_id) {
+    struct my_struct *s;
+
+    HASH_FIND_INT( users, &user_id, s );  /* s: output pointer */
+    return s;
+}
+
+void delete_user(struct my_struct *user) {
+    HASH_DEL( users, user);  /* user: pointer to deletee */
+    free(user);
+}
+
+void delete_all() {
+  struct my_struct *current_user, *tmp;
+
+  HASH_ITER(hh, users, current_user, tmp) {
+    HASH_DEL(users,current_user);  /* delete it (users advances to next) */
+    free(current_user);            /* free it */
+  }
+}
+
+void print_users() {
+    struct my_struct *s;
+
+    for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) {
+        printf("user id %d: name %s\n", s->id, s->name);
+    }
+}
+
+int name_sort(struct my_struct *a, struct my_struct *b) {
+    return strcmp(a->name,b->name);
+}
+
+int id_sort(struct my_struct *a, struct my_struct *b) {
+    return (a->id - b->id);
+}
+
+void sort_by_name() {
+    HASH_SORT(users, name_sort);
+}
+
+void sort_by_id() {
+    HASH_SORT(users, id_sort);
+}
+
+int main(int argc, char *argv[]) {
+    char in[10];
+    int id=1, running=1;
+    struct my_struct *s;
+    unsigned num_users;
+
+    while (running) {
+        printf("1. add user\n");
+        printf("2. find user\n");
+        printf("3. delete user\n");
+        printf("4. delete all users\n");
+        printf("5. sort items by name\n");
+        printf("6. sort items by id\n");
+        printf("7. print users\n");
+        printf("8. count users\n");
+        printf("9. quit\n");
+        gets(in);
+        switch(atoi(in)) {
+            case 1:
+                printf("name?\n");
+                add_user(id++, gets(in));
+                break;
+            case 2:
+                printf("id?\n");
+                s = find_user(atoi(gets(in)));
+                printf("user: %s\n", s ? s->name : "unknown");
+                break;
+            case 3:
+                printf("id?\n");
+                s = find_user(atoi(gets(in)));
+                if (s) delete_user(s);
+                else printf("id unknown\n");
+                break;
+            case 4:
+                delete_all();
+                break;
+            case 5:
+                sort_by_name();
+                break;
+            case 6:
+                sort_by_id();
+                break;
+            case 7:
+                print_users();
+                break;
+            case 8:
+                num_users=HASH_COUNT(users);
+                printf("there are %u users\n", num_users);
+                break;
+            case 9:
+                running=0;
+                break;
+        }
+    }
+
+    delete_all();  /* free any structures */
+    return 0;
+}
+
+

This program is included in the distribution in tests/example.c. You can run +make example in that directory to compile it easily.

+
+
+
+
+

Standard key types

+
+

This section goes into specifics of how to work with different kinds of keys. +You can use nearly any type of key-- integers, strings, pointers, structures, etc.

+
+ + + +
+
Note
+
+
A note about float
+

You can use floating point keys. This comes with the same caveats as with any +program that tests floating point equality. In other words, even the tiniest +difference in two floating point numbers makes them distinct keys.

+
+
+
+

Integer keys

+

The preceding examples demonstrated use of integer keys. To recap, use the +convenience macros HASH_ADD_INT and HASH_FIND_INT for structures with +integer keys. (The other operations such as HASH_DELETE and HASH_SORT are +the same for all types of keys).

+
+
+

String keys

+

If your structure has a string key, the operations to use depend on whether your +structure points to the key (char *) or the string resides within the +structure (char a[10]). This distinction is important. As we’ll see below, +you need to use HASH_ADD_KEYPTR when your structure points to a key (that is, +the key itself is outside of the structure); in contrast, use HASH_ADD_STR +for a string key that is contained within your structure.

+
+ + + +
+
Note
+
+
char[ ] vs. char*
+

The string is within the structure in the first example below-- name is a +char[10] field. In the second example, the key is outside of the +structure-- name is a char *. So the first example uses HASH_ADD_STR but +the second example uses HASH_ADD_KEYPTR. For information on this macro, see +the Macro reference.

+
+
+
+

String within structure

+
+
A string-keyed hash (string within structure)
+
+
#include <string.h>  /* strcpy */
+#include <stdlib.h>  /* malloc */
+#include <stdio.h>   /* printf */
+#include "uthash.h"
+
+struct my_struct {
+    char name[10];             /* key (string is WITHIN the structure) */
+    int id;
+    UT_hash_handle hh;         /* makes this structure hashable */
+};
+
+
+int main(int argc, char *argv[]) {
+    const char **n, *names[] = { "joe", "bob", "betty", NULL };
+    struct my_struct *s, *tmp, *users = NULL;
+    int i=0;
+
+    for (n = names; *n != NULL; n++) {
+        s = (struct my_struct*)malloc(sizeof(struct my_struct));
+        strncpy(s->name, *n,10);
+        s->id = i++;
+        HASH_ADD_STR( users, name, s );
+    }
+
+    HASH_FIND_STR( users, "betty", s);
+    if (s) printf("betty's id is %d\n", s->id);
+
+    /* free the hash table contents */
+    HASH_ITER(hh, users, s, tmp) {
+      HASH_DEL(users, s);
+      free(s);
+    }
+    return 0;
+}
+
+

This example is included in the distribution in tests/test15.c. It prints:

+
+
+
betty's id is 2
+
+
+
+

String pointer in structure

+

Now, here is the same example but using a char * key instead of char [ ]:

+
+
A string-keyed hash (structure points to string)
+
+
#include <string.h>  /* strcpy */
+#include <stdlib.h>  /* malloc */
+#include <stdio.h>   /* printf */
+#include "uthash.h"
+
+struct my_struct {
+    const char *name;          /* key */
+    int id;
+    UT_hash_handle hh;         /* makes this structure hashable */
+};
+
+
+int main(int argc, char *argv[]) {
+    const char **n, *names[] = { "joe", "bob", "betty", NULL };
+    struct my_struct *s, *tmp, *users = NULL;
+    int i=0;
+
+    for (n = names; *n != NULL; n++) {
+        s = (struct my_struct*)malloc(sizeof(struct my_struct));
+        s->name = *n;
+        s->id = i++;
+        HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s );
+    }
+
+    HASH_FIND_STR( users, "betty", s);
+    if (s) printf("betty's id is %d\n", s->id);
+
+    /* free the hash table contents */
+    HASH_ITER(hh, users, s, tmp) {
+      HASH_DEL(users, s);
+      free(s);
+    }
+    return 0;
+}
+
+

This example is included in tests/test40.c.

+
+
+
+

Pointer keys

+

Your key can be a pointer. To be very clear, this means the pointer itself +can be the key (in contrast, if the thing pointed to is the key, this is a +different use case handled by HASH_ADD_KEYPTR).

+

Here is a simple example where a structure has a pointer member, called key.

+
+
A pointer key
+
+
#include <stdio.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+typedef struct {
+  void *key;
+  int i;
+  UT_hash_handle hh;
+} el_t;
+
+el_t *hash = NULL;
+char *someaddr = NULL;
+
+int main() {
+  el_t *d;
+  el_t *e = (el_t*)malloc(sizeof(el_t));
+  if (!e) return -1;
+  e->key = (void*)someaddr;
+  e->i = 1;
+  HASH_ADD_PTR(hash,key,e);
+  HASH_FIND_PTR(hash, &someaddr, d);
+  if (d) printf("found\n");
+
+  /* release memory */
+  HASH_DEL(hash,e);
+  free(e);
+  return 0;
+}
+
+

This example is included in tests/test57.c. Note that the end of the program +deletes the element out of the hash, (and since no more elements remain in the +hash), uthash releases its internal memory.

+
+
+

Structure keys

+

Your key field can have any data type. To uthash, it is just a sequence of +bytes. Therefore, even a nested structure can be used as a key. We’ll use the +general macros HASH_ADD and HASH_FIND to demonstrate.

+
+ + + +
+
Note
+
Structures contain padding (wasted internal space used to fulfill +alignment requirements for the members of the structure). These padding bytes +must be zeroed before adding an item to the hash or looking up an item. +Therefore always zero the whole structure before setting the members of +interest. The example below does this-- see the two calls to memset.
+
+
+
A key which is a structure
+
+
#include <stdlib.h>
+#include <stdio.h>
+#include "uthash.h"
+
+typedef struct {
+  char a;
+  int b;
+} record_key_t;
+
+typedef struct {
+    record_key_t key;
+    /* ... other data ... */
+    UT_hash_handle hh;
+} record_t;
+
+int main(int argc, char *argv[]) {
+    record_t l, *p, *r, *tmp, *records = NULL;
+
+    r = (record_t*)malloc( sizeof(record_t) );
+    memset(r, 0, sizeof(record_t));
+    r->key.a = 'a';
+    r->key.b = 1;
+    HASH_ADD(hh, records, key, sizeof(record_key_t), r);
+
+    memset(&l, 0, sizeof(record_t));
+    l.key.a = 'a';
+    l.key.b = 1;
+    HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p);
+
+    if (p) printf("found %c %d\n", p->key.a, p->key.b);
+
+    HASH_ITER(hh, records, p, tmp) {
+      HASH_DEL(records, p);
+      free(p);
+    }
+    return 0;
+}
+
+

This usage is nearly the same as use of a compound key explained below.

+

Note that the general macros require the name of the UT_hash_handle to be +passed as the first argument (here, this is hh). The general macros are +documented in Macro Reference.

+
+
+
+
+

Advanced Topics

+
+
+

Compound keys

+

Your key can even comprise multiple contiguous fields.

+
+
A multi-field key
+
+
#include <stdlib.h>    /* malloc       */
+#include <stddef.h>    /* offsetof     */
+#include <stdio.h>     /* printf       */
+#include <string.h>    /* memset       */
+#include "uthash.h"
+
+#define UTF32 1
+
+typedef struct {
+  UT_hash_handle hh;
+  int len;
+  char encoding;      /* these two fields */
+  int text[];         /* comprise the key */
+} msg_t;
+
+typedef struct {
+    char encoding;
+    int text[];
+} lookup_key_t;
+
+int main(int argc, char *argv[]) {
+    unsigned keylen;
+    msg_t *msg, *tmp, *msgs = NULL;
+    lookup_key_t *lookup_key;
+
+    int beijing[] = {0x5317, 0x4eac};   /* UTF-32LE for 北京 */
+
+    /* allocate and initialize our structure */
+    msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) );
+    memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */
+    msg->len = sizeof(beijing);
+    msg->encoding = UTF32;
+    memcpy(msg->text, beijing, sizeof(beijing));
+
+    /* calculate the key length including padding, using formula */
+    keylen =   offsetof(msg_t, text)       /* offset of last key field */
+             + sizeof(beijing)             /* size of last key field */
+             - offsetof(msg_t, encoding);  /* offset of first key field */
+
+    /* add our structure to the hash table */
+    HASH_ADD( hh, msgs, encoding, keylen, msg);
+
+    /* look it up to prove that it worked :-) */
+    msg=NULL;
+
+    lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing));
+    memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing));
+    lookup_key->encoding = UTF32;
+    memcpy(lookup_key->text, beijing, sizeof(beijing));
+    HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg );
+    if (msg) printf("found \n");
+    free(lookup_key);
+
+    HASH_ITER(hh, msgs, msg, tmp) {
+      HASH_DEL(msgs, msg);
+      free(msg);
+    }
+    return 0;
+}
+
+

This example is included in the distribution in tests/test22.c.

+

If you use multi-field keys, recognize that the compiler pads adjacent fields +(by inserting unused space between them) in order to fulfill the alignment +requirement of each field. For example a structure containing a char followed +by an int will normally have 3 "wasted" bytes of padding after the char, in +order to make the int field start on a multiple-of-4 address (4 is the length +of the int).

+
+
+
Calculating the length of a multi-field key:
+

To determine the key length when using a multi-field key, you must include any +intervening structure padding the compiler adds for alignment purposes.

+

An easy way to calculate the key length is to use the offsetof macro from +<stddef.h>. The formula is:

+
+
+
key length =   offsetof(last_key_field)
+             + sizeof(last_key_field)
+             - offsetof(first_key_field)
+
+

In the example above, the keylen variable is set using this formula.

+
+

When dealing with a multi-field key, you must zero-fill your structure before +HASH_ADD'ing it to a hash table, or using its fields in a HASH_FIND key.

+

In the previous example, memset is used to initialize the structure by +zero-filling it. This zeroes out any padding between the key fields. If we +didn’t zero-fill the structure, this padding would contain random values. The +random values would lead to HASH_FIND failures; as two "identical" keys will +appear to mismatch if there are any differences within their padding.

+
+
+

Multi-level hash tables

+

A multi-level hash table arises when each element of a hash table contains its +own secondary hash table. There can be any number of levels. In a scripting +language you might see:

+
+
+
$items{bob}{age}=37
+
+

The C program below builds this example in uthash: the hash table is called +items. It contains one element (bob) whose own hash table contains one +element (age) with value 37. No special functions are necessary to build +a multi-level hash table.

+

While this example represents both levels (bob and age) using the same +structure, it would also be fine to use two different structure definitions. +It would also be fine if there were three or more levels instead of two.

+
+
Multi-level hash table
+
+
#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "uthash.h"
+
+/* hash of hashes */
+typedef struct item {
+  char name[10];
+  struct item *sub;
+  int val;
+  UT_hash_handle hh;
+} item_t;
+
+item_t *items=NULL;
+
+int main(int argc, char *argvp[]) {
+  item_t *item1, *item2, *tmp1, *tmp2;
+
+  /* make initial element */
+  item_t *i = malloc(sizeof(*i));
+  strcpy(i->name, "bob");
+  i->sub = NULL;
+  i->val = 0;
+  HASH_ADD_STR(items, name, i);
+
+  /* add a sub hash table off this element */
+  item_t *s = malloc(sizeof(*s));
+  strcpy(s->name, "age");
+  s->sub = NULL;
+  s->val = 37;
+  HASH_ADD_STR(i->sub, name, s);
+
+  /* iterate over hash elements  */
+  HASH_ITER(hh, items, item1, tmp1) {
+    HASH_ITER(hh, item1->sub, item2, tmp2) {
+      printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val);
+    }
+  }
+
+  /* clean up both hash tables */
+  HASH_ITER(hh, items, item1, tmp1) {
+    HASH_ITER(hh, item1->sub, item2, tmp2) {
+      HASH_DEL(item1->sub, item2);
+      free(item2);
+    }
+    HASH_DEL(items, item1);
+    free(item1);
+  }
+
+  return 0;
+}
+
+

The example above is included in tests/test59.c.

+
+
+

Items in several hash tables

+

A structure can be added to more than one hash table. A few reasons you might do +this include:

+
    +
  • +

    +each hash table may use an alternative key; +

    +
  • +
  • +

    +each hash table may have its own sort order; +

    +
  • +
  • +

    +or you might simply use multiple hash tables for grouping purposes. E.g., + you could have users in an admin_users and a users hash table. +

    +
  • +
+

Your structure needs to have a UT_hash_handle field for each hash table to +which it might be added. You can name them anything. E.g.,

+
+
+
UT_hash_handle hh1, hh2;
+
+
+
+

Items with alternative keys

+

You might create a hash table keyed on an ID field, and another hash table keyed +on username (if usernames are unique). You can add the same user structure to +both hash tables (without duplication of the structure), allowing lookup of a +user structure by their name or ID. The way to achieve this is to have a +separate UT_hash_handle for each hash to which the structure may be added.

+
+
A structure with two alternative keys
+
+
struct my_struct {
+    int id;                    /* usual key */
+    char username[10];         /* alternative key */
+    UT_hash_handle hh1;        /* handle for first hash table */
+    UT_hash_handle hh2;        /* handle for second hash table */
+};
+
+

In the example above, the structure can now be added to two separate hash +tables. In one hash, id is its key, while in the other hash, username is +its key. (There is no requirement that the two hashes have different key +fields. They could both use the same key, such as id).

+

Notice the structure has two hash handles (hh1 and hh2). In the code +below, notice that each hash handle is used exclusively with a particular hash +table. (hh1 is always used with the users_by_id hash, while hh2 is +always used with the users_by_name hash table).

+
+
Two keys on a structure
+
+
    struct my_struct *users_by_id = NULL, *users_by_name = NULL, *s;
+    int i;
+    char *name;
+
+    s = malloc(sizeof(struct my_struct));
+    s->id = 1;
+    strcpy(s->username, "thanson");
+
+    /* add the structure to both hash tables */
+    HASH_ADD(hh1, users_by_id, id, sizeof(int), s);
+    HASH_ADD(hh2, users_by_name, username, strlen(s->username), s);
+
+    /* lookup user by ID in the "users_by_id" hash table */
+    i=1;
+    HASH_FIND(hh1, users_by_id, &i, sizeof(int), s);
+    if (s) printf("found id %d: %s\n", i, s->username);
+
+    /* lookup user by username in the "users_by_name" hash table */
+    name = "thanson";
+    HASH_FIND(hh2, users_by_name, name, strlen(name), s);
+    if (s) printf("found user %s: %d\n", name, s->id);
+
+
+
+

Several sort orders

+

It comes as no suprise that two hash tables can have different sort orders, but +this fact can also be used advantageously to sort the same items in several +ways. This is based on the ability to store a structure in several hash tables.

+

Extending the previous example, suppose we have many users. We have added each +user structure to the users_by_id hash table and the users_by_name hash table. +(To reiterate, this is done without the need to have two copies of each structure). +Now we can define two sort functions, then use HASH_SRT.

+
+
+
int sort_by_id(struct my_struct *a, struct my_struct *b) {
+  if (a->id == b->id) return 0;
+  return (a->id < b->id) ? -1 : 1;
+}
+
+
+
+
int sort_by_name(struct my_struct *a, struct my_struct *b) {
+  return strcmp(a->username,b->username);
+}
+
+
+
+
HASH_SRT(hh1, users_by_id, sort_by_id);
+HASH_SRT(hh2, users_by_name, sort_by_name);
+
+

Now iterating over the items in users_by_id will traverse them in id-order +while, naturally, iterating over users_by_name will traverse them in +name-order. The items are fully forward-and-backward linked in each order. +So even for one set of users, we might store them in two hash tables to provide +easy iteration in two different sort orders.

+
+
+

Bloom filter (faster misses)

+

Programs that generate a fair miss rate (HASH_FIND that result in NULL) may +benefit from the built-in Bloom filter support. This is disabled by default, +because programs that generate only hits would incur a slight penalty from it. +Also, programs that do deletes should not use the Bloom filter. While the +program would operate correctly, deletes diminish the benefit of the filter. +To enable the Bloom filter, simply compile with -DHASH_BLOOM=n like:

+
+
+
-DHASH_BLOOM=27
+
+

where the number can be any value up to 32 which determines the amount of memory +used by the filter, as shown below. Using more memory makes the filter more +accurate and has the potential to speed up your program by making misses bail +out faster.

+
+ + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Bloom filter sizes for selected values of n
n Bloom filter size (per hash table)

16

8 kilobytes

20

128 kilobytes

24

2 megabytes

28

32 megabytes

32

512 megabytes

+
+

Bloom filters are only a performance feature; they do not change the results of +hash operations in any way. The only way to gauge whether or not a Bloom filter +is right for your program is to test it. Reasonable values for the size of the +Bloom filter are 16-32 bits.

+
+
+

Select

+

An experimental select operation is provided that inserts those items from a +source hash that satisfy a given condition into a destination hash. This +insertion is done with somewhat more efficiency than if this were using +HASH_ADD, namely because the hash function is not recalculated for keys of the +selected items. This operation does not remove any items from the source hash. +Rather the selected items obtain dual presence in both hashes. The destination +hash may already have items in it; the selected items are added to it. In order +for a structure to be usable with HASH_SELECT, it must have two or more hash +handles. (As described here, a structure can exist in many +hash tables at the same time; it must have a separate hash handle for each one).

+
+
+
user_t *users=NULL, *admins=NULL; /* two hash tables */
+
+
+
+
typedef struct {
+    int id;
+    UT_hash_handle hh;  /* handle for users hash */
+    UT_hash_handle ah;  /* handle for admins hash */
+} user_t;
+
+

Now suppose we have added some users, and want to select just the administrator +users who have id’s less than 1024.

+
+
+
#define is_admin(x) (((user_t*)x)->id < 1024)
+HASH_SELECT(ah,admins,hh,users,is_admin);
+
+

The first two parameters are the destination hash handle and hash table, the +second two parameters are the source hash handle and hash table, and the last +parameter is the select condition. Here we used a macro is_admin() but we +could just as well have used a function.

+
+
+
int is_admin(void *userv) {
+  user_t *user = (user_t*)userv;
+  return (user->id < 1024) ? 1 : 0;
+}
+
+

If the select condition always evaluates to true, this operation is +essentially a merge of the source hash into the destination hash. Of course, +the source hash remains unchanged under any use of HASH_SELECT. It only adds +items to the destination hash selectively.

+

The two hash handles must differ. An example of using HASH_SELECT is included +in tests/test36.c.

+
+
+

Built-in hash functions

+

Internally, a hash function transforms a key into a bucket number. You don’t +have to take any action to use the default hash function, currently Jenkin’s.

+

Some programs may benefit from using another of the built-in hash functions. +There is a simple analysis utility included with uthash to help you determine +if another hash function will give you better performance.

+

You can use a different hash function by compiling your program with +-DHASH_FUNCTION=HASH_xyz where xyz is one of the symbolic names listed +below. E.g.,

+
+
+
cc -DHASH_FUNCTION=HASH_BER -o program program.c
+
+
+ + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 2. Built-in hash functions
Symbol Name

JEN

Jenkins (default)

BER

Bernstein

SAX

Shift-Add-Xor

OAT

One-at-a-time

FNV

Fowler/Noll/Vo

SFH

Paul Hsieh

MUR

MurmurHash v3 (see note)

+
+
+ + + +
+
Note
+
+
MurmurHash
+

A special symbol must be defined if you intend to use MurmurHash. To use it, add +-DHASH_USING_NO_STRICT_ALIASING to your CFLAGS. And, if you are using +the gcc compiler with optimization, add -fno-strict-aliasing to your CFLAGS.

+
+
+
+

Which hash function is best?

+

You can easily determine the best hash function for your key domain. To do so, +you’ll need to run your program once in a data-collection pass, and then run +the collected data through an included analysis utility.

+

First you must build the analysis utility. From the top-level directory,

+
+
+
cd tests/
+make
+
+

We’ll use test14.c to demonstrate the data-collection and analysis steps +(here using sh syntax to redirect file descriptor 3 to a file):

+
+
Using keystats
+
+
% cc -DHASH_EMIT_KEYS=3 -I../src -o test14 test14.c
+% ./test14 3>test14.keys
+% ./keystats test14.keys
+fcn  ideal%     #items   #buckets  dup%  fl   add_usec  find_usec  del-all usec
+---  ------ ---------- ---------- -----  -- ---------- ----------  ------------
+SFH   91.6%       1219        256    0%  ok         92        131            25
+FNV   90.3%       1219        512    0%  ok        107         97            31
+SAX   88.7%       1219        512    0%  ok        111        109            32
+OAT   87.2%       1219        256    0%  ok         99        138            26
+JEN   86.7%       1219        256    0%  ok         87        130            27
+BER   86.2%       1219        256    0%  ok        121        129            27
+
+
+ + + +
+
Note
+
The number 3 in -DHASH_EMIT_KEYS=3 is a file descriptor. Any file descriptor +that your program doesn’t use for its own purposes can be used instead of 3. +The data-collection mode enabled by -DHASH_EMIT_KEYS=x should not be used in +production code.
+
+

Usually, you should just pick the first hash function that is listed. Here, this +is SFH. This is the function that provides the most even distribution for +your keys. If several have the same ideal%, then choose the fastest one +according to the find_usec column.

+
+
+

keystats column reference

+
+
+fcn +
+
+

+ symbolic name of hash function +

+
+
+ideal% +
+
+

+ The percentage of items in the hash table which can be looked up within an + ideal number of steps. (Further explained below). +

+
+
+#items +
+
+

+ the number of keys that were read in from the emitted key file +

+
+
+#buckets +
+
+

+ the number of buckets in the hash after all the keys were added +

+
+
+dup% +
+
+

+ the percent of duplicate keys encountered in the emitted key file. + Duplicates keys are filtered out to maintain key uniqueness. (Duplicates + are normal. For example, if the application adds an item to a hash, + deletes it, then re-adds it, the key is written twice to the emitted file.) +

+
+
+flags +
+
+

+ this is either ok, or nx (noexpand) if the expansion inhibited flag is + set, described in Expansion internals. It is not recommended + to use a hash function that has the noexpand flag set. +

+
+
+add_usec +
+
+

+ the clock time in microseconds required to add all the keys to a hash +

+
+
+find_usec +
+
+

+ the clock time in microseconds required to look up every key in the hash +

+
+
+del-all usec +
+
+

+ the clock time in microseconds required to delete every item in the hash +

+
+
+
+
+

ideal%

+
+
+
What is ideal%?
+

The n items in a hash are distributed into k buckets. Ideally each bucket +would contain an equal share (n/k) of the items. In other words, the maximum +linear position of any item in a bucket chain would be n/k if every bucket is +equally used. If some buckets are overused and others are underused, the +overused buckets will contain items whose linear position surpasses n/k. +Such items are considered non-ideal.

+

As you might guess, ideal% is the percentage of ideal items in the hash. These +items have favorable linear positions in their bucket chains. As ideal% +approaches 100%, the hash table approaches constant-time lookup performance.

+
+
+
+
+

hashscan

+
+ + + +
+
Note
+
This utility is only available on Linux, and on FreeBSD (8.1 and up).
+
+

A utility called hashscan is included in the tests/ directory. It +is built automatically when you run make in that directory. This tool +examines a running process and reports on the uthash tables that it finds in +that program’s memory. It can also save the keys from each table in a format +that can be fed into keystats.

+

Here is an example of using hashscan. First ensure that it is built:

+
+
+
cd tests/
+make
+
+

Since hashscan needs a running program to inspect, we’ll start up a simple +program that makes a hash table and then sleeps as our test subject:

+
+
+
./test_sleep &
+pid: 9711
+
+

Now that we have a test program, let’s run hashscan on it:

+
+
+
./hashscan 9711
+Address            ideal    items  buckets mc fl bloom/sat fcn keys saved to
+------------------ ----- -------- -------- -- -- --------- --- -------------
+0x862e038            81%    10000     4096 11 ok 16    14% JEN
+
+

If we wanted to copy out all its keys for external analysis using keystats, +add the -k flag:

+
+
+
./hashscan -k 9711
+Address            ideal    items  buckets mc fl bloom/sat fcn keys saved to
+------------------ ----- -------- -------- -- -- --------- --- -------------
+0x862e038            81%    10000     4096 11 ok 16    14% JEN /tmp/9711-0.key
+
+

Now we could run ./keystats /tmp/9711-0.key to analyze which hash function +has the best characteristics on this set of keys.

+
+

hashscan column reference

+
+
+Address +
+
+

+ virtual address of the hash table +

+
+
+ideal +
+
+

+ The percentage of items in the table which can be looked up within an ideal + number of steps. See [ideal] in the keystats section. +

+
+
+items +
+
+

+ number of items in the hash table +

+
+
+buckets +
+
+

+ number of buckets in the hash table +

+
+
+mc +
+
+

+ the maximum chain length found in the hash table (uthash usually tries to + keep fewer than 10 items in each bucket, or in some cases a multiple of 10) +

+
+
+fl +
+
+

+ flags (either ok, or NX if the expansion-inhibited flag is set) +

+
+
+bloom/sat +
+
+

+ if the hash table uses a Bloom filter, this is the size (as a power of two) + of the filter (e.g. 16 means the filter is 2^16 bits in size). The second + number is the "saturation" of the bits expressed as a percentage. The lower + the percentage, the more potential benefit to identify cache misses quickly. +

+
+
+fcn +
+
+

+ symbolic name of hash function +

+
+
+keys saved to +
+
+

+ file to which keys were saved, if any +

+
+
+
+
+
How hashscan works
+

When hashscan runs, it attaches itself to the target process, which suspends +the target process momentarily. During this brief suspension, it scans the +target’s virtual memory for the signature of a uthash hash table. It then +checks if a valid hash table structure accompanies the signature and reports +what it finds. When it detaches, the target process resumes running normally. +The hashscan is performed "read-only"-- the target process is not modified. +Since hashscan is analyzing a momentary snapshot of a running process, it may +return different results from one run to another.

+
+
+
+
+

Expansion internals

+

Internally this hash manages the number of buckets, with the goal of having +enough buckets so that each one contains only a small number of items.

+
+
+
Why does the number of buckets matter?
+

When looking up an item by its key, this hash scans linearly through the items +in the appropriate bucket. In order for the linear scan to run in constant +time, the number of items in each bucket must be bounded. This is accomplished +by increasing the number of buckets as needed.

+
+
+

Normal expansion

+

This hash attempts to keep fewer than 10 items in each bucket. When an item is +added that would cause a bucket to exceed this number, the number of buckets in +the hash is doubled and the items are redistributed into the new buckets. In an +ideal world, each bucket will then contain half as many items as it did before.

+

Bucket expansion occurs automatically and invisibly as needed. There is +no need for the application to know when it occurs.

+
+
Per-bucket expansion threshold
+

Normally all buckets share the same threshold (10 items) at which point bucket +expansion is triggered. During the process of bucket expansion, uthash can +adjust this expansion-trigger threshold on a per-bucket basis if it sees that +certain buckets are over-utilized.

+

When this threshold is adjusted, it goes from 10 to a multiple of 10 (for that +particular bucket). The multiple is based on how many times greater the actual +chain length is than the ideal length. It is a practical measure to reduce +excess bucket expansion in the case where a hash function over-utilizes a few +buckets but has good overall distribution. However, if the overall distribution +gets too bad, uthash changes tactics.

+
+
+
+

Inhibited expansion

+

You usually don’t need to know or worry about this, particularly if you used +the keystats utility during development to select a good hash for your keys.

+

A hash function may yield an uneven distribution of items across the buckets. +In moderation this is not a problem. Normal bucket expansion takes place as +the chain lengths grow. But when significant imbalance occurs (because the hash +function is not well suited to the key domain), bucket expansion may be +ineffective at reducing the chain lengths.

+

Imagine a very bad hash function which always puts every item in bucket 0. No +matter how many times the number of buckets is doubled, the chain length of +bucket 0 stays the same. In a situation like this, the best behavior is to +stop expanding, and accept O(n) lookup performance. This is what uthash +does. It degrades gracefully if the hash function is ill-suited to the keys.

+

If two consecutive bucket expansions yield ideal% values below 50%, uthash +inhibits expansion for that hash table. Once set, the bucket expansion +inhibited flag remains in effect as long as the hash has items in it. +Inhibited expansion may cause HASH_FIND to exhibit worse than constant-time +performance.

+
+
+
+

Hooks

+

You don’t need to use these hooks- they are only here if you want to modify +the behavior of uthash. Hooks can be used to change how uthash allocates +memory, and to run code in response to certain internal events.

+
+

malloc/free

+

By default this hash implementation uses malloc and free to manage memory. +If your application uses its own custom allocator, this hash can use them too.

+
+
Specifying alternate memory management functions
+
+
#include "uthash.h"
+
+/* undefine the defaults */
+#undef uthash_malloc
+#undef uthash_free
+
+/* re-define, specifying alternate functions */
+#define uthash_malloc(sz) my_malloc(sz)
+#define uthash_free(ptr,sz) my_free(ptr)
+
+...
+
+

Notice that uthash_free receives two parameters. The sz parameter is for +convenience on embedded platforms that manage their own memory.

+
+
+

Out of memory

+

If memory allocation fails (i.e., the malloc function returned NULL), the +default behavior is to terminate the process by calling exit(-1). This can +be modified by re-defining the uthash_fatal macro.

+
+
+
#undef uthash_fatal
+#define uthash_fatal(msg) my_fatal_function(msg);
+
+

The fatal function should terminate the process or longjmp back to a safe +place. Uthash does not support "returning a failure" if memory cannot be +allocated.

+
+
+

Internal events

+

There is no need for the application to set these hooks or take action in +response to these events. They are mainly for diagnostic purposes.

+

These two hooks are "notification" hooks which get executed if uthash is +expanding buckets, or setting the bucket expansion inhibited flag. Normally +both of these hooks are undefined and thus compile away to nothing.

+
+
Expansion
+

There is a hook for the bucket expansion event.

+
+
Bucket expansion hook
+
+
#include "uthash.h"
+
+#undef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) printf("expanded to %d buckets\n", tbl->num_buckets)
+
+...
+
+
+
+
Expansion-inhibition
+

This hook can be defined to code to execute in the event that uthash decides to +set the bucket expansion inhibited flag.

+
+
Bucket expansion inhibited hook
+
+
#include "uthash.h"
+
+#undef uthash_noexpand_fyi
+#define uthash_noexpand_fyi printf("warning: bucket expansion inhibited\n");
+
+...
+
+
+
+
+
+

Debug mode

+

If a program that uses this hash is compiled with -DHASH_DEBUG=1, a special +internal consistency-checking mode is activated. In this mode, the integrity +of the whole hash is checked following every add or delete operation. This is +for debugging the uthash software only, not for use in production code.

+

In the tests/ directory, running make debug will run all the tests in +this mode.

+

In this mode, any internal errors in the hash data structure will cause a +message to be printed to stderr and the program to exit.

+

The UT_hash_handle data structure includes next, prev, hh_next and +hh_prev fields. The former two fields determine the "application" ordering +(that is, insertion order-- the order the items were added). The latter two +fields determine the "bucket chain" order. These link the UT_hash_handles +together in a doubly-linked list that is a bucket chain.

+

Checks performed in -DHASH_DEBUG=1 mode:

+
    +
  • +

    +the hash is walked in its entirety twice: once in bucket order and a + second time in application order +

    +
  • +
  • +

    +the total number of items encountered in both walks is checked against the + stored number +

    +
  • +
  • +

    +during the walk in bucket order, each item’s hh_prev pointer is compared + for equality with the last visited item +

    +
  • +
  • +

    +during the walk in application order, each item’s prev pointer is compared + for equality with the last visited item +

    +
  • +
+
+
+
Macro debugging:
+

Sometimes it’s difficult to interpret a compiler warning on a line which +contains a macro call. In the case of uthash, one macro can expand to dozens of +lines. In this case, it is helpful to expand the macros and then recompile. +By doing so, the warning message will refer to the exact line within the macro.

+

Here is an example of how to expand the macros and then recompile. This uses the +test1.c program in the tests/ subdirectory.

+
+
+
gcc -E -I../src test1.c > /tmp/a.c
+egrep -v '^#' /tmp/a.c > /tmp/b.c
+indent /tmp/b.c
+gcc -o /tmp/b /tmp/b.c
+
+

The last line compiles the original program (test1.c) with all macros expanded. +If there was a warning, the referenced line number can be checked in /tmp/b.c.

+
+
+
+

Thread safety

+

You can use uthash in a threaded program. But you must do the locking. Use a +read-write lock to protect against concurrent writes. It is ok to have +concurrent readers (since uthash 1.5).

+

For example using pthreads you can create an rwlock like this:

+
+
+
pthread_rwlock_t lock;
+if (pthread_rwlock_init(&lock,NULL) != 0) fatal("can't create rwlock");
+
+

Then, readers must acquire the read lock before doing any HASH_FIND calls or +before iterating over the hash elements:

+
+
+
if (pthread_rwlock_rdlock(&lock) != 0) fatal("can't get rdlock");
+HASH_FIND_INT(elts, &i, e);
+pthread_rwlock_unlock(&lock);
+
+

Writers must acquire the exclusive write lock before doing any update. Add, +delete, and sort are all updates that must be locked.

+
+
+
if (pthread_rwlock_wrlock(&lock) != 0) fatal("can't get wrlock");
+HASH_DEL(elts, e);
+pthread_rwlock_unlock(&lock);
+
+

If you prefer, you can use a mutex instead of a read-write lock, but this will +reduce reader concurrency to a single thread at a time.

+

An example program using uthash with a read-write lock is included in +tests/threads/test1.c.

+
+
+
+
+

Macro reference

+
+
+

Convenience macros

+

The convenience macros do the same thing as the generalized macros, but +require fewer arguments.

+

In order to use the convenience macros,

+
    +
  1. +

    +the structure’s UT_hash_handle field must be named hh, and +

    +
  2. +
  3. +

    +for add or find, the key field must be of type int or char[] or pointer +

    +
  4. +
+
+ + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 3. Convenience macros
macro arguments

HASH_ADD_INT

(head, keyfield_name, item_ptr)

HASH_FIND_INT

(head, key_ptr, item_ptr)

HASH_ADD_STR

(head, keyfield_name, item_ptr)

HASH_FIND_STR

(head, key_ptr, item_ptr)

HASH_ADD_PTR

(head, keyfield_name, item_ptr)

HASH_FIND_PTR

(head, key_ptr, item_ptr)

HASH_DEL

(head, item_ptr)

HASH_SORT

(head, cmp)

HASH_COUNT

(head)

+
+
+
+

General macros

+

These macros add, find, delete and sort the items in a hash. You need to +use the general macros if your UT_hash_handle is named something other +than hh, or if your key’s data type isn’t int or char[].

+
+ + +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 4. General macros
macro arguments

HASH_ADD

(hh_name, head, keyfield_name, key_len, item_ptr)

HASH_ADD_KEYPTR

(hh_name, head, key_ptr, key_len, item_ptr)

HASH_FIND

(hh_name, head, key_ptr, key_len, item_ptr)

HASH_DELETE

(hh_name, head, item_ptr)

HASH_SRT

(hh_name, head, cmp)

HASH_CNT

(hh_name, head)

HASH_CLEAR

(hh_name, head)

HASH_SELECT

(dst_hh_name, dst_head, src_hh_name, src_head, condition)

HASH_ITER

(hh_name, head, item_ptr, tmp_item_ptr)

+
+
+ + + +
+
Note
+
HASH_ADD_KEYPTR is used when the structure contains a pointer to the +key, rather than the key itself.
+
+
+

Argument descriptions

+
+
+hh_name +
+
+

+ name of the UT_hash_handle field in the structure. Conventionally called + hh. +

+
+
+head +
+
+

+ the structure pointer variable which acts as the "head" of the hash. So + named because it initially points to the first item that is added to the hash. +

+
+
+keyfield_name +
+
+

+ the name of the key field in the structure. (In the case of a multi-field + key, this is the first field of the key). If you’re new to macros, it + might seem strange to pass the name of a field as a parameter. See + note. +

+
+
+key_len +
+
+

+ the length of the key field in bytes. E.g. for an integer key, this is + sizeof(int), while for a string key it’s strlen(key). (For a + multi-field key, see the notes in this guide on calculating key length). +

+
+
+key_ptr +
+
+

+ for HASH_FIND, this is a pointer to the key to look up in the hash + (since it’s a pointer, you can’t directly pass a literal value here). For + HASH_ADD_KEYPTR, this is the address of the key of the item being added. +

+
+
+item_ptr +
+
+

+ pointer to the structure being added, deleted, or looked up, or the current + pointer during iteration. This is an input parameter for HASH_ADD and + HASH_DELETE macros, and an output parameter for HASH_FIND and + HASH_ITER. (When using HASH_ITER to iterate, tmp_item_ptr + is another variable of the same type as item_ptr, used internally). +

+
+
+cmp +
+
+

+ pointer to comparison function which accepts two arguments (pointers to + items to compare) and returns an int specifying whether the first item + should sort before, equal to, or after the second item (like strcmp). +

+
+
+condition +
+
+

+ a function or macro which accepts a single argument-- a void pointer to a + structure, which needs to be cast to the appropriate structure type. The + function or macro should return (or evaluate to) a non-zero value if the + structure should be "selected" for addition to the destination hash. +

+
+
+
+
+
+
+
+

+ + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.pdf b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.pdf new file mode 120000 index 0000000000..7eb72b0450 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/userguide.pdf @@ -0,0 +1 @@ +../pdf/userguide.pdf \ No newline at end of file diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utarray.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utarray.html new file mode 100644 index 0000000000..b3901d7d7b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utarray.html @@ -0,0 +1,596 @@ + + + + + +utarray: dynamic array macros for C + + + + + + + +
+
+
+uthash at SourceForge.net +
+ uthash home + >> utarray macros +
+
+
+
+

Introduction

+
+
+ +

A set of general-purpose dynamic array macros for C structures are included with +uthash in utarray.h. To use these macros in your own C program, just +copy utarray.h into your source directory and use it in your programs.

+
+
+
#include "utarray.h"
+
+

The dynamic array supports basic operations such as push, pop, and erase on the +array elements. These array elements can be any simple datatype or structure. +The array operations are based loosely on the C++ STL vector methods.

+

Internally the dynamic array contains a contiguous memory region into which +the elements are copied. This buffer is grown as needed using realloc to +accomodate all the data that is pushed into it.

+
+

Download

+

To download the utarray.h header file, follow the link on the +uthash home page.

+
+
+

BSD licensed

+

This software is made available under the +revised BSD license. +It is free and open source.

+
+
+

Platforms

+

The utarray macros have been tested on:

+
    +
  • +

    +Linux, +

    +
  • +
  • +

    +Mac OS X, +

    +
  • +
  • +

    +Windows, using Visual Studio 2008 and Visual Studio 2010 +

    +
  • +
+
+
+
+
+

Usage

+
+
+

Declaration

+

The array itself has the data type UT_array, regardless of the type of +elements to be stored in it. It is declared like,

+
+
+
UT_array *nums;
+
+
+
+

New and free

+

The next step is to create the array using utarray_new. Later when you’re +done with the array, utarray_free will free it and all its elements.

+
+
+

Push, pop, etc

+

The central features of the utarray involve putting elements into it, taking +them out, and iterating over them. There are several operations +to pick from that deal with either single elements or ranges of elements at a +time. In the examples below we will use only the push operation to insert +elements.

+
+
+
+
+

Elements

+
+

Support for dynamic arrays of integers or strings is especially easy. These are +best shown by example:

+
+

Integers

+

This example makes a utarray of integers, pushes 0-9 into it, then prints it. +Lastly it frees it.

+
+
Integer elements
+
+
#include <stdio.h>
+#include "utarray.h"
+
+int main() {
+  UT_array *nums;
+  int i, *p;
+
+  utarray_new(nums,&ut_int_icd);
+  for(i=0; i < 10; i++) utarray_push_back(nums,&i);
+
+  for(p=(int*)utarray_front(nums);
+      p!=NULL;
+      p=(int*)utarray_next(nums,p)) {
+    printf("%d\n",*p);
+  }
+
+  utarray_free(nums);
+
+  return 0;
+}
+
+

The second argument to utarray_push_back is always a pointer to the type +(so a literal cannot be used). So for integers, it is an int*.

+
+
+

Strings

+

In this example we make a utarray of strings, push two strings into it, print +it and free it.

+
+
String elements
+
+
#include <stdio.h>
+#include "utarray.h"
+
+int main() {
+  UT_array *strs;
+  char *s, **p;
+
+  utarray_new(strs,&ut_str_icd);
+
+  s = "hello"; utarray_push_back(strs, &s);
+  s = "world"; utarray_push_back(strs, &s);
+  p = NULL;
+  while ( (p=(char**)utarray_next(strs,p))) {
+    printf("%s\n",*p);
+  }
+
+  utarray_free(strs);
+
+  return 0;
+}
+
+

In this example, since the element is a char*, we pass a pointer to it +(char**) as the second argument to utarray_push_back. Note that "push" makes +a copy of the source string and pushes that copy into the array.

+
+
+

About UT_icd

+

Arrays be made of any type of element, not just integers and strings. The +elements can be basic types or structures. Unless you’re dealing with integers +and strings (which use pre-defined ut_int_icd and ut_str_icd), you’ll need +to define a UT_icd helper structure. This structure contains everything that +utarray needs to initialize, copy or destruct elements.

+
+
+
typedef struct {
+    size_t sz;
+    init_f *init;
+    ctor_f *copy;
+    dtor_f *dtor;
+} UT_icd;
+
+

The three function pointers init, copy, and dtor have these prototypes:

+
+
+
typedef void (ctor_f)(void *dst, const void *src);
+typedef void (dtor_f)(void *elt);
+typedef void (init_f)(void *elt);
+
+

The sz is just the size of the element being stored in the array.

+

The init function will be invoked whenever utarray needs to initialize an +empty element. This only happens as a byproduct of utarray_resize or +utarray_extend_back. If init is NULL, it defaults to zero filling the +new element using memset.

+

The copy function is used whenever an element is copied into the array. +It is invoked during utarray_push_back, utarray_insert, utarray_inserta, +or utarray_concat. If copy is NULL, it defaults to a bitwise copy using +memcpy.

+

The dtor function is used to clean up an element that is being removed from +the array. It may be invoked due to utarray_resize, utarray_pop_back, +utarray_erase, utarray_clear, utarray_done or utarray_free. If the +elements need no cleanup upon destruction, dtor may be NULL.

+
+
+

Scalar types

+

The next example uses UT_icd with all its defaults to make a utarray of +long elements. This example pushes two longs, prints them, and frees the +array.

+
+
long elements
+
+
#include <stdio.h>
+#include "utarray.h"
+
+UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
+
+int main() {
+  UT_array *nums;
+  long l, *p;
+  utarray_new(nums, &long_icd);
+
+  l=1; utarray_push_back(nums, &l);
+  l=2; utarray_push_back(nums, &l);
+
+  p=NULL;
+  while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p);
+
+  utarray_free(nums);
+  return 0;
+}
+
+
+
+

Structures

+

Structures can be used as utarray elements. If the structure requires no +special effort to initialize, copy or destruct, we can use UT_icd with all +its defaults. This example shows a structure that consists of two integers. Here +we push two values, print them and free the array.

+
+
Structure (simple)
+
+
#include <stdio.h>
+#include "utarray.h"
+
+typedef struct {
+    int a;
+    int b;
+} intpair_t;
+
+UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
+
+int main() {
+
+  UT_array *pairs;
+  intpair_t ip, *p;
+  utarray_new(pairs,&intpair_icd);
+
+  ip.a=1;  ip.b=2;  utarray_push_back(pairs, &ip);
+  ip.a=10; ip.b=20; utarray_push_back(pairs, &ip);
+
+  for(p=(intpair_t*)utarray_front(pairs);
+      p!=NULL;
+      p=(intpair_t*)utarray_next(pairs,p)) {
+    printf("%d %d\n", p->a, p->b);
+  }
+
+  utarray_free(pairs);
+  return 0;
+}
+
+

The real utility of UT_icd is apparent when the elements of the utarray are +structures that require special work to initialize, copy or destruct.

+

For example, when a structure contains pointers to related memory areas that +need to be copied when the structure is copied (and freed when the structure is +freed), we can use custom init, copy, and dtor members in the UT_icd.

+

Here we take an example of a structure that contains an integer and a string. +When this element is copied (such as when an element is pushed into the array), +we want to "deep copy" the s pointer (so the original element and the new +element point to their own copies of s). When an element is destructed, we +want to "deep free" its copy of s. Lastly, this example is written to work +even if s has the value NULL.

+
+
Structure (complex)
+
+
#include <stdio.h>
+#include <stdlib.h>
+#include "utarray.h"
+
+typedef struct {
+    int a;
+    char *s;
+} intchar_t;
+
+void intchar_copy(void *_dst, const void *_src) {
+  intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
+  dst->a = src->a;
+  dst->s = src->s ? strdup(src->s) : NULL;
+}
+
+void intchar_dtor(void *_elt) {
+  intchar_t *elt = (intchar_t*)_elt;
+  if (elt->s) free(elt->s);
+}
+
+UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
+
+int main() {
+  UT_array *intchars;
+  intchar_t ic, *p;
+  utarray_new(intchars, &intchar_icd);
+
+  ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic);
+  ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic);
+
+  p=NULL;
+  while( (p=(intchar_t*)utarray_next(intchars,p))) {
+    printf("%d %s\n", p->a, (p->s ? p->s : "null"));
+  }
+
+  utarray_free(intchars);
+  return 0;
+}
+
+
+
+
+
+

Reference

+
+

This table lists all the utarray operations. These are loosely based on the C++ +vector class.

+
+

Operations

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

utarray_new(UT_array *a, UT_icd *icd)

allocate a new array

utarray_free(UT_array *a)

free an allocated array

utarray_init(UT_array *a,UT_icd *icd)

init an array (non-alloc)

utarray_done(UT_array *a)

dispose of an array (non-allocd)

utarray_reserve(UT_array *a,int n)

ensure space available for n more elements

utarray_push_back(UT_array *a,void *p)

push element p onto a

utarray_pop_back(UT_array *a)

pop last element from a

utarray_extend_back(UT_array *a)

push empty element onto a

utarray_len(UT_array *a)

get length of a

utarray_eltptr(UT_array *a,int j)

get pointer of element from index

utarray_eltidx(UT_array *a,void *e)

get index of element from pointer

utarray_insert(UT_array *a,void *p, int j)

insert element p to index j

utarray_inserta(UT_array *a,UT_array *w, int j)

insert array w into array a at index j

utarray_resize(UT_array *dst,int num)

extend or shrink array to num elements

utarray_concat(UT_array *dst,UT_array *src)

copy src to end of dst array

utarray_erase(UT_array *a,int pos,int len)

remove len elements from a[pos]..a[pos+len-1]

utarray_clear(UT_array *a)

clear all elements from a, setting its length to zero

utarray_sort(UT_array *a,cmpfcn *cmp)

sort elements of a using comparison function

utarray_find(UT_array *a,void *v, cmpfcn *cmp)

find element v in utarray (must be sorted)

utarray_front(UT_array *a)

get first element of a

utarray_next(UT_array *a,void *e)

get element of a following e (front if e is NULL)

utarray_prev(UT_array *a,void *e)

get element of a before e (back if e is NULL)

utarray_back(UT_array *a)

get last element of a

+
+
+
+

Notes

+
    +
  1. +

    +utarray_new and utarray_free are used to allocate a new array and free it, + while utarray_init and utarray_done can be used if the UT_array is already + allocated and just needs to be initialized or have its internal resources + freed. +

    +
  2. +
  3. +

    +utarray_reserve takes the "delta" of elements to reserve (not the total + desired capacity of the array-- this differs from the C++ STL "reserve" notion) +

    +
  4. +
  5. +

    +utarray_sort expects a comparison function having the usual strcmp -like + convention where it accepts two elements (a and b) and returns a negative + value if a precedes b, 0 if a and b sort equally, and positive if b precedes a. + This is an example of a comparison function: +

    +
    +
    +
    int intsort(const void *a,const void*b) {
    +    int _a = *(int*)a;
    +    int _b = *(int*)b;
    +    return _a - _b;
    +}
    +
    +
  6. +
  7. +

    +utarray_find uses a binary search to locate an element having a certain value + according to the given comparison function. The utarray must be first sorted + using the same comparison function. An example of using utarray_find with + a utarray of strings is included in tests/test61.c. +

    +
  8. +
  9. +

    +A pointer to a particular element (obtained using utarray_eltptr or + utarray_front, utarray_next, utarray_prev, utarray_back) becomes invalid whenever + another element is inserted into the utarray. This is because the internal + memory management may need to realloc the element storage to a new address. + For this reason, it’s usually better to refer to an element by its integer + index in code whose duration may include element insertion. +

    +
  10. +
+
+
+
+
+

+ + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utlist.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utlist.html new file mode 100644 index 0000000000..2df6950c4a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utlist.html @@ -0,0 +1,465 @@ + + + + + +utlist: linked list macros for C structures + + + + + + + +
+
+
+uthash at SourceForge.net +
+ uthash home + >> utlist macros +
+
+
+
+

Introduction

+
+
+ +

A set of general-purpose linked list macros for C structures are included with +uthash in utlist.h. To use these macros in your own C program, just +copy utlist.h into your source directory and use it in your programs.

+
+
+
#include "utlist.h"
+
+

These macros support the basic linked list operations: adding and deleting +elements, sorting them and iterating over them.

+
+

Download

+

To download the utlist.h header file, follow the link on the +uthash home page.

+
+
+

BSD licensed

+

This software is made available under the +revised BSD license. +It is free and open source.

+
+
+

Platforms

+

The utlist macros have been tested on:

+
    +
  • +

    +Linux, +

    +
  • +
  • +

    +Mac OS X, and +

    +
  • +
  • +

    +Windows, using Visual Studio 2008, Visual Studio 2010, or Cygwin/MinGW. +

    +
  • +
+
+
+
+
+

Using utlist

+
+
+

Types of lists

+

Three types of linked lists are supported:

+
    +
  • +

    +singly-linked lists, +

    +
  • +
  • +

    +doubly-linked lists, and +

    +
  • +
  • +

    +circular, doubly-linked lists +

    +
  • +
+
+

Efficiency

+

For all types of lists, prepending elements and deleting elements are +constant-time operations. Appending to a singly-linked list is an O(n) +operation but appending to a doubly-linked list is constant time using these +macros. (This is because, in the utlist implementation of the doubly-linked +list, the head element’s prev member points back to the list tail, even when +the list is non-circular). Sorting is an O(n log(n)) operation. Iteration +and searching are O(n) for all list types.

+
+
+
+

List elements

+

You can use any structure with these macros, as long as the structure +contains a next pointer. If you want to make a doubly-linked list, +the element also needs to have a prev pointer.

+
+
+
typedef struct element {
+    char *name;
+    struct element *prev; /* needed for a doubly-linked list only */
+    struct element *next; /* needed for singly- or doubly-linked lists */
+} element;
+
+

You can name your structure anything. In the example above it is called element. +Within a particular list, all elements must be of the same type.

+
+
+

List head

+

The list head is simply a pointer to your element structure. You can name it +anything. It must be initialized to NULL.

+
+
+
element *head = NULL;
+
+
+
+

List operations

+

The lists support inserting or deleting elements, sorting the elements and +iterating over them.

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Singly-linked Doubly-linked Circular, doubly-linked

LL_PREPEND(head,add);

DL_PREPEND(head,add);

CDL_PREPEND(head,add;

LL_APPEND(head,add);

DL_APPEND(head,add);

LL_CONCAT(head1,head2);

DL_CONCAT(head1,head2);

LL_DELETE(head,del);

DL_DELETE(head,del);

CDL_DELETE(head,del);

LL_SORT(head,cmp);

DL_SORT(head,cmp);

CDL_SORT(head,cmp);

LL_FOREACH(head,elt) {…}

DL_FOREACH(head,elt) {…}

CDL_FOREACH(head,elt) {…}

LL_FOREACH_SAFE(head,elt,tmp) {…}

DL_FOREACH_SAFE(head,elt,tmp) {…}

CDL_FOREACH_SAFE(head,elt,tmp1,tmp2) {…}

LL_SEARCH_SCALAR(head,elt,mbr,val);

DL_SEARCH_SCALAR(head,elt,mbr,val);

CDL_SEARCH_SCALAR(head,elt,mbr,val);

LL_SEARCH(head,elt,like,cmp);

DL_SEARCH(head,elt,like,cmp);

CDL_SEARCH(head,elt,like,cmp);

+
+

Prepend means to insert an element in front of the existing list head (if any), +changing the list head to the new element. Append means to add an element at the +end of the list, so it becomes the new tail element. Concatenate takes two +properly constructed lists and appends the second list to the first. (Visual +Studio 2008 does not support LL_CONCAT and DL_CONCAT, but VS2010 is ok.)

+

The sort operation never moves the elements in memory; rather it only adjusts +the list order by altering the prev and next pointers in each element. Also +the sort operation can change the list head to point to a new element.

+

The foreach operation is for easy iteration over the list from the head to the +tail. A usage example is shown below. You can of course just use the prev and +next pointers directly instead of using the foreach macros. +The foreach_safe operation should be used if you plan to delete any of the list +elements while iterating.

+

The search operation is a shortcut for iteration in search of a particular +element. It is not any faster than manually iterating and testing each element. +There are two forms: the "scalar" version searches for an element using a +simple equality test on a given structure member, while the general version takes an +element to which all others in the list will be compared using a cmp function.

+

The parameters shown in the table above are explained here:

+
+
+head +
+
+

+ The list head (a pointer to your list element structure). +

+
+
+add +
+
+

+ A pointer to the list element structure you are adding to the list. +

+
+
+del +
+
+

+ A pointer to the list element structure you are deleting from the list. +

+
+
+elt +
+
+

+ A pointer that will be assigned to each list element in succession (see + example) in the case of iteration macros; also, the output pointer from + the search macros. +

+
+
+like +
+
+

+ An element pointer, having the same type as elt, for which the search macro + seeks a match (if found, the match is stored in elt). A match is determined + by the given cmp function. +

+
+
+cmp +
+
+

+ pointer to comparison function which accepts two arguments-- these are + pointers to two element structures to be compared. The comparison function + must return an int that is negative, zero, or positive, which specifies + whether the first item should sort before, equal to, or after the second item, + respectively. (In other words, the same convention that is used by strcmp). + Note that under Visual Studio 2008 you may need to declare the two arguments + as void * and then cast them back to their actual types. +

+
+
+tmp +
+
+

+ A pointer of the same type as elt. Used internally. Need not be initialized. +

+
+
+mbr +
+
+

+ In the scalar search macro, the name of a member within the elt structure which + will be tested (using ==) for equality with the value val. +

+
+
+val +
+
+

+ In the scalar search macro, specifies the value of (of structure member + field) of the element being sought. +

+
+
+
+
+

Example

+

This example program reads names from a text file (one name per line), and +appends each name to a doubly-linked list. Then it sorts and prints them.

+
+
A doubly-linked list
+
+
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "utlist.h"
+
+#define BUFLEN 20
+
+typedef struct el {
+    char bname[BUFLEN];
+    struct el *next, *prev;
+} el;
+
+int namecmp(el *a, el *b) {
+    return strcmp(a->bname,b->bname);
+}
+
+el *head = NULL; /* important- initialize to NULL! */
+
+int main(int argc, char *argv[]) {
+    el *name, *elt, *tmp, etmp;
+
+    char linebuf[BUFLEN];
+    FILE *file;
+
+    if ( (file = fopen( "test11.dat", "r" )) == NULL ) {
+        perror("can't open: ");
+        exit(-1);
+    }
+
+    while (fgets(linebuf,BUFLEN,file) != NULL) {
+        if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1);
+        strncpy(name->bname,linebuf,BUFLEN);
+        DL_APPEND(head, name);
+    }
+    DL_SORT(head, namecmp);
+    DL_FOREACH(head,elt) printf("%s", elt->bname);
+
+    memcpy(&etmp.bname, "WES\n", 5);
+    DL_SEARCH(head,elt,&etmp,namecmp);
+    if (elt) printf("found %s\n", elt->bname);
+
+    /* now delete each element, use the safe iterator */
+    DL_FOREACH_SAFE(head,elt,tmp) {
+      DL_DELETE(head,elt);
+    }
+
+    fclose(file);
+
+    return 0;
+}
+
+
+
+
+
+

+ + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utstring.html b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utstring.html new file mode 100644 index 0000000000..b5015fc6ed --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/html/utstring.html @@ -0,0 +1,353 @@ + + + + + +utstring: dynamic string macros for C + + + + + + + +
+
+
+uthash at SourceForge.net +
+ uthash home + >> utstring macros +
+
+
+
+

Introduction

+
+
+ +

A set of very basic dynamic string macros for C programs are included with +uthash in utstring.h. To use these macros in your own C program, just +copy utstring.h into your source directory and use it in your programs.

+
+
+
#include "utstring.h"
+
+

The dynamic string supports basic operations such as inserting data (including +binary data-- despite its name, utstring is not limited to string content), +concatenation, getting the length and content, and clearing it. The string +operations are listed below.

+
+

Download

+

To download the utstring.h header file, follow the link on the +uthash home page.

+
+
+

BSD licensed

+

This software is made available under the +revised BSD license. +It is free and open source.

+
+
+

Platforms

+

The utstring macros have been tested on:

+
    +
  • +

    +Linux, +

    +
  • +
  • +

    +Windows, using Visual Studio 2008 and Visual Studio 2010 +

    +
  • +
+
+
+
+
+

Usage

+
+
+

Declaration

+

The dynamic string itself has the data type UT_string. It is declared like,

+
+
+
UT_string *str;
+
+
+
+

New and free

+

The next step is to create the string using utstring_new. Later when you’re +done with it, utstring_free will free it and all its content.

+
+
+

Manipulation

+

The utstring_printf or utstring_bincpy operations insert (copy) data into +the string. To concatenate one utstring to another, use utstring_concat. To +clear the content of the string, use utstring_clear. The length of the string +is available from utstring_len, and its content from utstring_body. This +evaluates to a char*. The buffer it points to is always null-terminated. +So, it can be used directly with external functions that expect a string. +This automatic null terminator is not counted in the length of the string.

+
+
+

Samples

+

These examples show how to use utstring.

+
+
Sample 1
+
+
#include <stdio.h>
+#include "utstring.h"
+
+int main() {
+    UT_string *s;
+
+    utstring_new(s);
+    utstring_printf(s, "hello world!" );
+    printf("%s\n", utstring_body(s));
+
+    utstring_free(s);
+    return 0;
+}
+
+

The next example is meant to demonstrate that printf appends to the string. +It also shows concatenation.

+
+
Sample 2
+
+
#include <stdio.h>
+#include "utstring.h"
+
+int main() {
+    UT_string *s, *t;
+
+    utstring_new(s);
+    utstring_new(t);
+
+    utstring_printf(s, "hello " );
+    utstring_printf(s, "world " );
+
+    utstring_printf(t, "hi " );
+    utstring_printf(t, "there " );
+
+    utstring_concat(s, t);
+    printf("length: %u\n", utstring_len(s));
+    printf("%s\n", utstring_body(s));
+
+    utstring_free(s);
+    utstring_free(t);
+    return 0;
+}
+
+

The last example shows how binary data can be inserted into the string. It also +clears the string and prints new data into it.

+
+
Sample 3
+
+
#include <stdio.h>
+#include "utstring.h"
+
+int main() {
+    UT_string *s;
+    char binary[] = "\xff\xff";
+
+    utstring_new(s);
+    utstring_bincpy(s, binary, sizeof(binary));
+    printf("length is %u\n", utstring_len(s));
+
+    utstring_clear(s);
+    utstring_printf(s,"number %d", 10);
+    printf("%s\n", utstring_body(s));
+
+    utstring_free(s);
+    return 0;
+}
+
+
+
+
+
+

Reference

+
+

These are the utstring operations.

+
+

Operations

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

utstring_new(s)

allocate a new utstring

utstring_renew(s)

allocate a new utstring (if s is NULL) otherwise clears it

utstring_free(s)

free an allocated utstring

utstring_init(s)

init a utstring (non-alloc)

utstring_done(s)

dispose of a utstring (non-allocd)

utstring_printf(s,fmt,…)

printf into a utstring (appends)

utstring_bincpy(s,bin,len)

insert binary data of length len (appends)

utstring_concat(dst,src)

concatenate src utstring to end of dst utstring

utstring_clear(s)

clear the content of s (setting its length to 0)

utstring_len(s)

obtain the length of s as an unsigned integer

utstring_body(s)

get char* to body of s (buffer is always null-terminated)

+
+
+
+

Notes

+
    +
  1. +

    +utstring_new and utstring_free are used to allocate a new string and free it, + while utstring_init and utstring_done can be used if the UT_string is already + allocated and just needs to be initialized or have its internal resources + freed. +

    +
  2. +
  3. +

    +utstring_printf is actually a function defined statically in utstring.h + rather than a macro. +

    +
  4. +
+
+
+
+
+

+ + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/pdf/userguide.pdf b/ami_test_client/libc-jss/uthash-1.9.6/doc/pdf/userguide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..38e37bc77d7ee62c87ac19651df26ca2768afe7d GIT binary patch literal 315233 zcmcG12Rzl^|G$W&tOjLX8i;VWm08LzBP823!X1*ZaK2dA`PZy|?Y(t-KR1ielLI{B>0`0~{s}bFw(X zAR_}4(}9WYb<%Z$iS337y5cO|?40nTa3PqiECUX21$_xc{IZ||%+}o17G{TsDG8DO zh+b?acZ-?HD5sMkO{Z^b?gq0o$HVO0U{+3YGgC?~Wl97&g>VFEP8C;ZHfwWNx7qpN z$fZn(q?iyxIiWes!Op_P+{K%0R_sz{MUl^n5GT#5B&H-R3^T`D!JHk;-K?El99?G~ zAF(KNz?PElDvEMn5i@+!#ksn{T;1*5aI=#xiBD(>YG)XwsIwb9za(~qc!i~yb%s}aac*wVoVGXz=h+#T#3u>z8BsHQ z(#E+uxw}~6X1Rm<8+S7-(#_pvmf5H!um(XXJm?wL=-T37_BijE{X_qaBeUWN=9@F)3|sW@c1PU_lg@4< zdI?-Xh*CT@W`-*ei3Cez3pB`K_PH@j;0;2QLeViZyitI8I=NWE%q^VU-C&M5M<a(aFlr+74$0 z^R%^dz=30Ar@QMc^CbSpJj(Sb#6n_*gZs^037SRP7?`t@9o`M+0#|PI1Ly{(0Vu${$^XXD8M8(K3E+rXX2L9;9GwZ0 ziSsgt{zXRiqO2rNODQ3<@I}WBY_&N!ExXw&LC>%ekUfpb=Ir|7 zu!lG$rQMi?JPKAG=D;3V!E~LR?JUVeV=0&iJ%@#}phpSl2Czs3K_;Jc2`Y!4BX5`) zmD6x{aI@R#01h38X_)3_Zs9<#B1?fk=sDJZ7XJWy=Loz!AaT&KX1>T8O94Y@QA+;J zqM_+EAjyWAI{=e}pFVCVMFhJ9twf7baQ=@6-h3^oiPPn-)G1O5tzNTM?5M3@Ca ziVjXrjxcLGh;rCrLC6~h|8TT(b;Y?J7MitxO94P=QA*1?i-2^%*=y-WHro=k5G_jK zXu)RzkfJ*vcPHQ~0ocz6Yw#taA+W+6X`h8d`nGnKwv-FN>|6mfrHB?R5eh*IQCKa+ zEEIy|#Le8z6`VZ|?v8kv3(gwnf&=!ICF=?;wUuPtV>c{ z3hxv#3w)rhcLn&3Y`(wobk?|{MI|XW9Wje|NR$3$FP4ITfDIETcd8My0BDbwvpL=s z;1dYwL%QlPt4W9@!XIe*Igc=le>6dS2G|)KX`R{qSrqsRfQHwoVRYaTdhicuLL#@C1|sIg5f+@V0gq09#qjE=Bebe`E5D zv4K;>1QD}fXrGglJ=uo;75`9>J11a(m_7z|ax|g4}gNECup_Euf@z7W3@JS-9IkPCgmW7RNgj45OrX7WL=?Z7>Hu4RAZ$ zW}`8P#W4?2S}9y5#4Oy=Ft>DZTF4qN9^jd!bB=$6m<2paPI$m`JIKJp9HAx1xW5#v zGsi1J%wnCrI6ML3%$@bG=!Tr+Ip`fR3vm=&Y}_4zqyit>)zZbznKD9!SPa}4A+V5i zYQtb+%64x1V8&#L0t5-Rz+_|?#ME(k8-Sk>NEB@PKL#*@07=A@LC_lq;#fdd7(ku@ zxDGgTM+UFfnDfP&Gzx12Cy$*LisC<4ELYOOwu1F5%ifLGuUo_Pc#ruQl*9@#mF8 zTpk_P1^75R7AqRtn$022(fB|Er-o4OSJuD%Nl4|907X7Ag1GP0r|EtF%?GwCHi(& zfS(fL3}X9mb~d(dFgTh)OwrEG6`W)QM*gXyXGd+nb3<>=MU?^9x@B)j8?RNo>Zt3Ra0)vYKQJR&eeKr01 z(o7ZN^v85%(-X3-9tVaW46DqW> zbN19uO61j~8pm;$t$A9>{qFe|8)f&lzL8aBaoxT5h1+zBH@B$iI&m9*DSlATgghx! zaWFBB>vNjXf$MJ-SPc_}T2s@GqwOCa6vxBTLvpcB#v^_A1T^!t>l+>fyfN<$;O*?8 z9nLs&LBj3&;rPBGKee-4E}zb6FlVi5sZ@t6vmQl+B#g_26xW+4VLfF!lSR7woxakD zaouQGKx`z45kvz2cEe`0Q_R5d5NszLBZ?Hq!X(ATMX>-c;oTh^jLErAasU<_jmC&# z0O-b|B}K)7t(!lX82KX%Rt8cE{sD{vS{%q36uH!fet?4?AjCuX0Reu1Tx4y?W{45x zj4(>Y5quE~$>Q|i_PaP)>HuJ51hincGE7Vt=jBF}LnRo+BS?}{K{Poi;G`_oEw=Bs`Z=8O zut*V`?Nr0W*WG#p1GiuC6<~%GHyR=ZZ&g{!@w@9?dd=STb*%3-2aAq3DjB0!_RT@tQ2Onk zyJahdCfF-qrJrWCLcYHo#7*^{=KQxmyJd3YHnVg!_8ZG={p4+hh(6K%-2LbGDXY@t z?```cPf|0ds(DDb8~#WceSVX_`u?rS9)}v;BBs;<$wWT=?%uDxwPTL2S_=etQ1bgu z+2%Ron1r|SCcWl-+R%7^S7Fu#@&4{+Zq~b&=&)VCKJ6}QuDa2iWXaEWk@@_ISFax6 z^G{`X_Q48-0%$VNhK@(FOL0jNIRx;jhxPLWzs)$ zR<1T@rzxQtwwJvp>zgeHGxKkjwfX*MrC8weriCkxb z*x;KUzt*bLT#I`zzWrJVCJT{pE|DfUrfS2@hh?oX8FlOwNjLo=UPtjW=YWi7R_g^PQ}O8sWkG~_%wT{ zo>2H?K3Z( zxj^R?-rNvWzUZS5Pjw|3LU&(i-)-+$Lam&`%AaXfbMke^vklA}i~D=UDnvBTo=&1~ z)8<8fR_KYCOuKE8m+#npk-eaS&E)fgfoFHRcZjVO;>-J0pzB{aJXJE^&-9HxjK0u?)k$=cdjPqCdp;O;kKZu+w ze5sfbF8|EIP!dzB7*YFe!=P;4WJCEKwShL41D*|2B25BKhkDXwG=B%MsQrp;7md>O zn51bhKz*ZU&1A3XyRb6Z;MSqMBQK4lIsJAR!F}ojjaEtW=N~vbETYko=`teXLzOagYtIkc$72UH z%w7)mBz$x~=s3deIxLZyNTazb_sTQ8Q-sHT;}^|?`644A?B6_mgKg;H;{h>A@5>jH z?VWmK-&+_(i$)mFJ@?vveLYIAFj$vwLI{M2Et5zZq275nO1PR}>>NK&kVVAB@F*3)K>!qg*? zkfr|5*jh{h57OPP0J$It!A))OIgN%8lG8v4AqfR^px;kpAp{}d2>m?mSRo{*aS)Oa zPt=AX386yh6T*7X=k$C?LL?CSd3rvi`1E`b;+y{7^n6Hh;(Ty|vj9WF2@eK7iSrS{ zZ(ttyG<`@oaXvzX4f;KCJ~(kcIB`BgfDHVd5CnrhiSr?*=_5Z@ruTx!_1LVdSI{1BJ^-8Xyr+1V_R}!Tw>el7K+$zbTuJ_mERY3`i~`WoSSd0_+*s2ebqX zg8`L_2-Lqxn~ocilSa(IEhA}2geV&L&2R~EQ6$h%3<@cVl7#=8w&~OoIc>yr(K65` zi4;WxNQMFa9TsRY90S5~z^Y&nAl-ysT-eCvjg*2WCy*RmPMgX9Q5n#Z5~5h(*1-XP z#W4sNMjR`OL16y9dDBTVa@yvj_5YPNET9d5Z#V`iDhWeNNJ8H3zbTszEzVLlZKapT zvJp)P8Y3<$0a-Q_T2vf_)X-SCD0T^(Hl3&=Cym_KSRRI=P@sLGnxz{cqZ)(_!SaK?m6Ku#|XMp(H_q z47gTsKobI3IJ7v(Qi?C4sl*`mT)R7c-Y*YXL`g$|GXTgxI75M6Vz4ljB&4VRJQGMk zW%4aszB7Sn+)&^U07wFk1?MLS_@l63RpjDl!mMor-y`PS$mv`5g8>D!6A6qk9MV=4 z0)|3^S{d?DyAFeFd~)Z*es zB5b~-5m5H>k_JvF0EvL{K!{2LaE}D401U{#xJfGYCF3Rm`7aM`L@h-EIE2AMQC_fZ z097J^7ND_<8{Jtooh=+DCvf>r1rmWk6|jJx5P>M*xgdcmU{H&=34l6E0_WLaQmMi+ zfHtl ze;yA2AQLJz$oabL#{&X@BA~7aG-OR6_9B24UR+l=f4g!S%ZsFoqg_6Me!nb?} z1&KfesK^1a5Cm`C1gH)HwcP)< z%X7UOB&pPB8N`pMxp1HlPzo511yK?x904{JzBqGdj4gc;%C|ncjtMCpf+RH(*fz@=}_rRHtO%`#u3pyZbo1q^Lap5Xi458#&g-zExh8?fMh#U*{bTtE+ZX3IEP4 z{6j0R^tt-v)A27IR^LCd({Zm@S<{}N&V6ph&m^je6Z9X{Ej}MPte>5EDWAvMc}ibs z%`p!Xfh;#Ut+(lP=hxq)ld$7R37~pPU86)#U+uQOw&~N;7cExbpV8k+zH*AIdWF96 z81)LKto03h^)FvppKd?MO@EXAl2d<@YX~2``1>9ichSB6;VD@rc{=U!bbtDeM;MIn zG8WUNTA804T)-2+a!xE={7eOZquiG>XWkjS?O}e?uNZr`!PtiB)DwowH<=x`&|j?U z#oXDoKZ0&8i^yx5;ghgW3EC$(w&-fH8*VeMtj2bndgFmI?Z_~6{s_N(_GGV+j>(IY z48wiOcaH8qbxe{fmr>dMu;n?%k-FRa?k9FUq#fNk5LJE_&JxNLpO<{jT&tKd!4JJV zlWJ^-e0AgOn$L`Y6Oofn^O{U6YcRrze@MY0(B=&8@ zPkt?320;v!0KIt3=7wWO)N@u?N_*jQulrCP$ytw9TnT&9cq}pA>-P&-dr~Q1){0*S z)KYJy{a@jJV?-ZDNsQ+2IrjZ#(`h>91F)N_A9sADd-PhoUYF6S>mK8v_?c_BxVC4M zb@^S`Exm*8oBs~FM*X#NA4m3>*bMKY>+F3VQ6ts!IC(|zDD9&Vsk`gMVnnUMK%;~s zt#ztJ4EJ;-VLWC>-ph*Wd6?dQc(t`Q@QmtHHH~}huEL>bhLX?UV!+0Ngv>`@X<=>) z$0OKdY-y@2*5)xH;{)fKWEI)^Mo!_9>f5?6;Y@TVM8qr%*M4>|Ip>?{xY)?znn{<@Pn z=jpYr+BxrIDiP}bn^3*$6IWbj{xaYm+Mu8lFVkFEi(tqetzP{g^|S5;C%(5gYLmx* z(TG+(PL*ECCi`5DRrl@=ew$8q^Mj%v)fzQfuUF_PD&A%1+1M<7csSC&E4bvykG!II z&-J_SGW4k2^ZZGTvEfQ$X$^~jv8?ng{&rR5@Yn{UZ;DD3h_s5~t=~9rsXgQS>fM*f9lPXnlO3aWH8VR*$XNSV+EW$p zKGVu5vN^ltXJDX=@=3Aa){XZ46WOoPsothG$u%@PmFRPydOq45Y5Q`$UUx`ZwMfOS zJ9N}F0#dnH6B83V+Ib({bUq;dwBee!>s$3pc{SzT`EOrn6moaO4`yFYZ0Gj$ z!zp=w(r&iPy4HWNh68t4HrhJZ#V7S-?qH+BTc>xjaV;)&@2tb8^orZPeH^e2w!<+^ zlAXEm4{6Ei-&9=B;Wlrqwdji7qABuVZ%SH3;_rJ@K~yeBAD3<%V=wEtXt_Z83??n2*c}Vy>q5Kv6o>1iqeG=ybKH~J>iSrR^ zR<$Xrp{DnYbRz$6QB_i!4Y&p3Q2qn>Gbj+0#)7giNz5WBoCq}LVQ*3i`!aw?A_T$$ z2n2#ha1f6M)hRIG;e$Ze;_e@bu=(K{f>bTfj*&BC^bLIBbu zkRYlGAT9<(FyVj67*P^9&uu2yhGpe02FO1GN*e*mgZz3BHV0nnKb=0(WoEL?BiO6u zr448SAtjCm0YXqBjzLKR2ld~AH&0(lH!+q$Ux{V}1C#=kB@?zy5?D420)*qRizqBn zTj#mSq#Gd1Ko(I~(Lf*2;2?p5_(1xh0 zvJ@x*kbNjP12TXxG|&OeU-CvIS(_iTBwY$xM$$m3iUhE}a103f0vLe?l^EE6r;JE< zNXa-#z%0wlQ6TvUC{7Mo3I}x#XgCriofkhDX7+7f#E5{SmX$UX&;Uuu=EA|g0V9F} z_ZuXTiw9(8Xqy-BAYIp5KAwU=5eSikKpUV@0*eB9N-Rjy{PR2j4=~q)knV{s18v09 z9tG-Y#6hYJ0a9&{E}%duzvNN^ZGgJ@7LI^}mxr$;`hfgHS#bpP36e)BK&LozsRYhD z>E5QVgUM(ENX(uO&#gpF1gk&A6z1yUr;g?0qXytnl3b` z1VVwLI5_s7kwB0Yo+o|FcSaDoiv-052*?tHng^)X5D6;d5Xi;PFY?VJT_+~nJc66G zJlrL19#97qi2|unB=|ijqFj`_GiHT&6PcX2<*T$I?Hf3U0DuB1^MxH9vdgLSpcCod z>GByqP!db1F+f1-0M1H~-}`IEe9q?0vn&J`epzJ?xDWt90m}k#Fz~8D?eAaH=Ch(V zfA^rU!t&5Y6g?=liUq~Vz>t7Xz+X`MxU`fxz{LyH-hk&@ZVJJ{CIQNw;lQLwfH(>Q z11Mcs@Sub~1$#kN(Ola{x~05)0tneWC~*$e-+=Q20i4CZCC=v%I1eQgLPX2MW0LYB zz|lr1cL6pKSQ!LBFN>tl$y9g#&Mn`mK_U!*&*{WD5G6G56hVKAMHA;_q|Mti(k=33 zU@GyvfJ%VHv5*f7tO*49K#7nfcJTvkR%z$$8zHv0JPaif2UGwub%at|=o1SfJAcWR z&k{F(=a%oNAbS5$xhQlG6sq|G;Y_Hob+Nbvk*;}O1tI#mygLR8ZIGaJ2@VQ@38`{W z1c~@}j(mX>G^xSBGKv~F^-;hZBiteYjwq-V1snKJk7t4GGpVz|GSEgm9)K30fb$Br z3Xp_>ff6BrO%?@Bv!)FYH{YoswMQV32PL2h$%dH(^xv;TA_+MLl29xu0X>^@7E=Zm z0zf`OP9%Q5hGzv_!!~}GbJ|O{J(yQNZnIL%bdW}DWuyB%ps zTUT8S+*-NyN~9UrpGncoH7e0|+lE)eP8j!fwK1!Gy2JgcD7m=kVp6GUeAJ{jN7~aj zPgIoT8ZOb+O$kO^N`K-#$aF;KJEs%R_WEDxpSho!FsFWaTa%7WfjP6Kz|Q6bNYqWC zo+Wv+jCn_<(Yo=AL|v(<(JA-w9lWuB*Huu9i-V(|JL71lh)UX z>r>76qkOagr@-oXUS*+r`_C>vYQiswIdtoMS$XC1Duq*tW$aM(Av;EBcxgqyLs zmeRy)O=eB}p|3$I!jMFp}@9DDeKdY2lvYt$mt8Up)~!Gs3$ZwSqp4H?zY6B*MxvE1wzKa?Ad}uJAl_n?tO(=%g!O{(4Q-V`d@_ zye!uO_eKS5yqEt$fVNAeT$@Q=^oEGW_K&atmX=r8w5Z5kRz24YFgxjZVtcl(`;6IT zDH9y!RrI9>c6ej46s;0sV2@rBpRAxhJ&*JinRlr{Ho}E;p&j?v<`t`Hos(TxTGVDS|DvS3I?bs%2 zCn6<}3p(Ul?VQE7^TwsxjiNh`O_l5)+u3?bsVb%H*fv#$U45n__cyJ@##i#12wik+ z7*(qKYBJ;=fKN>AyY^JONCI_8?!K^+%EcF3tScJ~)P7NaJyvM(;U2rkd(_KlYMCwj zY~<)I&tHg=uqYWFP$;>p^2cYzov(R@ylC}wEsh&|vIe?ALXLH*S^m#%==Aka4zS1KBcb1N?q9=^gUP7TB)L> zZ<}>=xZ58Y`Hgzuu%moUf^_JihMmoWN`-R#D}GAzKKPQj?^@XvwkG2h=BKDSkI~$i z+WxFBJLl9^zhfNgzM1}sZmXRFK|PZpE`^^T~dv?dqqI z{;YIAU@xAZDR_Hy?SajC-z{&yb*_)Usha<$tU!8uqVJDGCfTQd$j}|D6L>d~&3(N3 z-jJ-#7SoeC@3gkOx`!N#eAPQjlT$KERmwiH;oE4X^1E6Z!xxd!8{1H)R)>`C{BSeI zr%bG+F->?^TUB`p-9ZJl<68_oB9y9XhG~@xuW&Y*Q0J_t4OFJJoFTrFn-j59`X-Q&i9W#eblS_7~pH-645vf06#d zyAGE-dxh1nDxVx<39i+(89rkhA|@am8M4)Xr)tqe|F&~~F7;jG(H?nv#(n>esS7=h z4+B3|l@{w(SZ79i-3nG^kx{H>+iSkou#D&9)9ZK6Xnv1~JXZLzzpm~w&$h|iT~h`g zJrT0jvT7qWQ_ue}_X})iSs)t@A{w)Yz{1&Z!Z|^i4JWkp`5!KmLO=z|{*qs+ocpcP{K4d*!Npbv@fHYi%^yteg)KH1ghWu_2JgJVq>hw-BYQgagj`@S zdE9G}mC@jKB1jM|Fn9@EMgvy?#2ywHOs(1| ze#atXQJ|z%64X@B9gCdXaEC%Eh_VS?EJ6WwpF5bMynm5}!F=FS;{qZ==(|HQ6^jjq z7M{QC5+oA=BB|H~S-h0NKo`O7n|TX^Yc6CKWJ%~AWF!d>3apGWaD1SuamZ*c3Ee}H z;8}x3LibqEU}>r7-oP>HDrVj90aJzkxC%W>|;#4zwFtuJ{?hjP1Tb*Zr zL-ma>D{A~_)7{vjU|hB9|csN%XQue_t@^G4U)%rK4GAXSqex#1SEQOZYi&RV1g%dz{lSB7Ufmp(NV z%;7U06+DX_d8sARytPfRA?e{SpBlwI$(yT_>C#y7@*m8ssydzOHgVj^2 z=-i}sXvOimuAN0zeR92FYD~-*^p~G*c!@VZ7&&k+?w$K&&N$G0s%~xR z%i-3BA@@g*o^4VUH9BtF@x8ez+scs75w@*;)$#9Iy020Xq?~xRZZv!SHH=Tt>*SVC z>p!kS>h(&{-#a+C^;XoEVszL3u+PSIpG+Fqizj}YJ?z8%3G?gS^z*ij{}I|I&GM^9 zjGmm>Jf3sE)KlkN%Hg!{cR#$n`MWBk_zoTOhZOEzCKs)K;WAbq377me`Xjb`Pq(^R zw0qGSxv-n;pL#d({V1nB${KTP$g2XXKy2H z7M>rZC2k_cDqzwvqJ8AYht`OZIY$NYxg0ws~9oIk1DP>hd5CX&-^Nl z?J~RB1|{#kC!|{Ro6852`SJ~Rf0H(^-&}LnR;%g2)WJ}XWIWXy;UQM;p^CLVTz7(R z`mN{E=$Ptg#}+l+_Fs2Q?Ab_1s^@XmP}kjiwtaVh=I4B(mVw$mw{TzSqm%U8d9Phg z-O$<4oH8WZ8dRLJL*><8g_WzfoUv|(`POeXlC%xteQmov#O^pt>@f$`Yj$JC=`sDZ zs#BYWecy-iObEWo7gA+MIv_d1WG z5y`odf7UTi^1gr!QV<vf2_MaK{3#Mi&k@8W&ZjzQ76{k(U%zS38(pW zB>M`^_QT5PCJrgf)^1jXb+}dBhY_tks}E$I-E^rsJLl!_k^N5<6L(3BM{FB9f((z@yed&xlD=?#Oiyp34SFZy zc{DT44dvhV?r`M~*9*azBcqHWoK$^JN&9tf*F@fr5bD{lVMio70Q{%ieUftP2_pDi+RGOVI3`juM;SBbpapCE*=RlC@x z7}F{a(%%E;XqDEyf9*4fdRVBV`n2YAV6}EGW9QKAgq<&h-Ri$6s;wRykX7A%dgbK9 zE1V|M1?vyn)4qR{QL);;vv6CY*-&3J`{2e@nM;1h`Qp9U*&X{|t`F6>6mYB*i4%)X z{4IG@p*WMp-(PV2XC&0YM^SC*n9^`=twtOM;Gkc zZ^RTp<4b4(F$KcAYQ%KJ^t4r={{=xecbpXQC0wGmZjKH@l0+LZw1iol&ggDNkO++8}6wr+0DQetA$ubmN{esvajxtR@46pwS8OE5Xy`mY3o zJBxt08G=zD;|)RS|Gxy2dmR6fU^u9*LW49Tp{W5>$po@CP#-81_yoz3rSGt#IcTBl zB;{shEAM6x-m>EgUe>q3-je$_|CLlIz7LpBA{7DEz5plc{}-v`VS@iiDyVz`m(QWv z2&jGt>L3cztU%#G$vi06fwCn_R4$_2y16)cTM;)EdKs8A=*8|T3f>i<9_ zXvYj*lCvO{GZ12b#B*ArMstWNsEGgTpg9LHsfIVWac2oyU_5k^68MlL$AzwHB9f+ zf2er#Q(k_h5Xq4Ufzly984Khp6-lzEJ;#Nk}4_j3G3eNbO82c5ll&xakor1a( zsSvq3&3l7L-I<@fUYP+|gEUV+h%gRx)Fbu>WbZp->vZa}*zaTIUq!fbvo$8YkZB@@ zkGf!C$n6hvqa91vUEIUZ#2lwxPPE zo`>qIMC+dF=zT2xj!oa|@YD|_)uqDYn0al|UcK|zsuI)}@llRI^7*G}KgEr%Oxh~+ z@H>63YJ#><&$XMHy8E(Pe{Sd$5#3X1{^dIN>c9``bt|bkb)=Mf{C9owdS|;j^aysO zQAYjiUU9+uN=HswAh_@OdGl{_V?zp6W?wOn`0ed1ak=Hya7uRIt?+C4?cEJ_CvJXo zk&_A5S`&9woO0y}JVUZ0_G2d{Xc2$7V_T^eR>@CgpDrT4c=zjU9E? zwBO_Lu>Y~|u!+U0eLUS>*(WN{@4g)3H@F)bW7{Jsd=j=1Z@ zxqf?`rQR7cpc&eHcvRy+hgo8LaHFwKC(3&~E{M9!tSQ$ZF+-3s(TktTuAA$|6E0PA z8zIB!m&X(;E01dnJ8-x^fE)bjlrooj{;_=1puA9Iz4n7Zvy)19B4OyJ&_}ncnVxGl zZBSd2#pcV^_^!p#c8A?-k6rDo+yW|I1@8u|B{%lPswmRCe6gN-e$jK!ytjp2P> zYAcI1uWYl>7DiQ{<1%4&c}~T-%Ktq}7gw$+CRL$b>CD4b=D~xv9Tf`mueh7;taYq- zvW@Fn)yc`!HnX}9-}Cr($gdhZo8ckox+=_rJvde=^I2+t=xHRzD|Y<;EhFc1mbOuE zI-PoAr*sD@yCX@*W0$J#)h56?=Q4}xPbPy zDX&Ejg*|t~j@ME&?}MM00( zt}Cj^Gky4>isMFp`-X{w?*1LD4?b3lIQ?0fy^>3TQ7AE18k6=p*X(c&8!6Qbe z+UR5iuU8CC)w9*C5Zdiy{A%3|iMA8%Z&7+B*BN^!8rhjdk5=bPvWmEJIs6PA)7qFq zogr`TbHrJe!|rEp=ty0;^jj2HqVL5wN%O3f4!U^~G9@pM!t zWYY4#@2ILv+;ulfmrg?3`=KK9=1%;Jyc}ueB!e!4hly`*@un9iBl9(T;3{#gzA}1CNBcZ6KwqAo|TumsJZ9%msXUd zUOkuerWs#zk@*A_&9|N6)Efqp`1>xhO>#BXHwC5)^IcoXo4Sfu(Q|d?$`y%cE18pS z_J?oy=zX-@iOvhg{Xi)>{rUx^YgyUv4EBn!`LfAW?yV9qt>t_t;1qJP-Z(e>q(o#? z73Y)HpK~3HMZP_~^TSp5^!X5lk5xQuM##V|oz9*cdB%BO%9TvM50Q*l7Fn<89k2Lf z^Q6<5>!xGUfngI*QId^aBl3YeG?=qeZFl9Byp_*Kjovo(sb!?*T;u7G+_V+5wlaUu z`MNN#vDP1B>PfeA@Ba|;YZHs3)s^}kd$s?ufYiBqXG4*x{yQmchSKj%V^>_7j518D zE;u(X_R@7UcOPnfmQ4CvO^fYOHQ8R}v}dkF?$Xwzhnbg#UD9z9V0o3}Q*^Ud+cWZ6 zbep0Po}Sa6mkasx+~%NP_G$&iC%f}D-&vJoo?cn-Yhq+0)(3Xi^F+W8DnZ+O{Ta5I zQp5U@&pXXCtJ(Lp4ols`Gjzpke--?dAhBux_5ER<;<8`HTpO%BQgYoM_f_9HK%EZH zX+u7os`{p4LOpRONxHGD)qKmEuT~C!#IL8_D3)`*z}b5BTHc;W%*i~_D#=p8gX%R$ zE=~9$UDd$Y3s>h+%3lG@|SosU2eO0167%1Gw z=MXMdozlds`07`5^5HT`l^)c0wXonvZS8eecDJN+->8dzcF4_dW!$t~`5uHEey&G7rpl}6@anyNhEzlR>6bS?4qsf8?zDzm ztu?*Y5!oETdGtz4?zf=KyqjUPyHKn@@hSX`3Myt;`)7lU(Vvt2SGe!H{p8*m#}~WI z@`RZzjrJ+*GGMjrY~^Y_oAIlbF~%b)84|t9WiiWr--i9Yu`QN z3UAf<;QT&(&aSr4zFgcqNk7H!@x6MuHftQFl-apENNTWT_>xT3pF|e=FtL2ROi_M! zcyFKDZ^H@ygG0&^3xwtuXkIwyl_%5Q17&C)T-^c@@ui05QPb@>K)-WRAAIO))!dN! z^zTTXEJaX-7!iRH0^&>Q2M^K-;C3je#s_yopet~o1Oi-TAY1?jFFgV=mie96HK3QY zna;kIld^OPMQDIeA`Wsoi5>En^im}hMK8g*qe=0Q#eylM*Hx0LaVVCa84TUGTF7Ue z>!p*^O&Sc{D_bb2LFnqg*h7;BgCHe{FU)s%DH_-<_RJvQiC&-@2Sw3hZn44O76=sa zpXX}N3tcVn%uo-0G)U~sA50$7Uu0zrsE7jv{&UAtTt{4ND5%;;Eie{bBb^gYS%7FX z)UXr;hUYE3gt1WlkvLGqxnofjjaU|;8U;0LU!XRQ;u6v#V?kOCT%nv_G=-WM=v&~4 zq39^|5{bD~6B^+BLoz{sKF~#VA;~0$TcNrcO`5-b*~iVrPktKzN_fle=q9D zE>mX}8#&#Ux1(>lzM&HTxQ*iNk5Kj6q~O2a&wuo% z@M)VX#mHf*G@)DlS7e;8tiF0dNoZ@m#rrr~Ubb*;Dc_`>pD#Vd)XKUxH`@0Z?087y zDWrUg_VaGRYmKF!!e1DA%Of6KYvZ}yI*La&rM0|th$`?`3A)?1B{FH3!9JD8=E8+l zle{I9_?4_Y>l&sm?J?kB$7ie%A$H_hae-UE(kWZLLFT%N%xC)|L`t|}IcC~a z?C-vWJNvt>vM32X5#AOOF~**#J$FUcGPZ)P4sx^ zb{8wa_NIK@TUed-<+Z!+OGY8v-w2)m+Ht7D^6?8aVpkZmd$&g*=4iu}$NsYM*|z9O z+|!RDEnM=g?5dTY&IctvP{$;~8N|gixrw?`>_ZyuUrH=~ahdXUww`z3nj<0R? z>v(>5Y+-}-hJNLcuDc(kwB<$D;LZGVo#d0EpJu|%$~rK>KYj%n>y-d$uTsawn4nxhx?<64C*%0XHCPQQ0E z?ka}qhxXYEG?r)=s1&xiwYln#MNNO#CZBq86zWwDY^@fyzNExm2CJMobEHNhHW~NtmQB(%!OS_ z4lvtp*&ZpxfirS4A8cL;(6_93%;KF;H= zp}CGNJkM|or*694mDsv2w^Mg5b4-$jE(_Q&O?A|IU%nq=w_e{ncXLXef%>B?`4Pd} zMaj42>W#~jHT(gsuP>`z*mZ0@ZoBDCuKwdm>V@a`d#k)PL-lrO#K3$M)rO|xGjsA~ zco-7jwYB-7rdqc7vgpNe2A7T+Bn#2iAknp3>Cd*xXa?>7)fmr;RjTdZ?rT2r1SjS@ za@)ahqI^ZLaXix-lXLW%^%ukAn;z9fe#9W(bQH$Bl}kBYikI%}`bIpxsJAy~+yn7-f&!D8)7Fs_x%<`Q6O6fhLM8;Yja;_0*|Qd3ueT z)s+1|n z3gKq%+Ra>!T=@=@p?6-M4?TEv`{Ua$R_r`R-7=s*zOx#ov*VFs%PV#1wjSO)nyoY` ze>e^byjNs!X}T8L*3dVhoWh!g3qazJUNzR%+Z?%FL3jP1TH(l)0W%Aa%Z{*Hmk`8Bnkp+dH;zYcd$ zPc{#~^Oe7c8B(~ka?N>t7ZK-fJLcE-)wAIb=w8H>#g9CAZy=Nazw<3jRcNzqPRV0T zN)R>u6}5V;2m31~jvBpGjyD$xdtM;al_UuJQ`Lws8r(nZVBb@lF}CK<>2=b11_$59 z+8L>z{F9==ITg*mMu&gGp<(=9uKMd2DH*ts7Z2h_+eCz&Q?rJQ%VSN>^7mdjK%+hN zDKprq?m^y`Bw7u0!`V373*QP{XpB7>JXl{-_0{(oA9!OJnVON-vOPWIMAmK^rSxO@ zyUuR6Kx=Psh_*XXvBq?bD(Cfz`_E;KwwCCwq?>wJ*~SoTek#~tiv!N0=;36Zw=Kg| zjBDoJ2nL6Hqi3;xVB2RMnY_ZkB5EC6_3tA;}&+G@h5jZ zZdRNSs^G9_whMM&%_2~xa-9>~=;v%9vNOc-i)&wy;*PT4ZbhNYUX#h9+jrpiy_m>O zu8GEYMz|I{xV|DYDD;Gu1IttI#^e3%kJ1YmS%1iu6t->E%)g8B;I1y8jJL|GTgTgF zc=|&flha6ILaxi?JEia=B4!_*RSss2-J#z!(t4Qw;tk=OiYFK!<`q_8gpcUv1P>k) z={Qi`{$Aj9^%UM%%QuqxLjIeow~5icPwUREf3_!eAnIIOWi_{#+$MjTb-cwZR?rpR z_Kzsd6=@Ncz8F}$<(^epc^O|?-;wcek4kM7Y~xo~nVagZ-Q?xT+9PD9u@57-k=j`4 zsNY7u!0?2&fj7^sLs~`7*EDv8MEG3{H>SC{YN%F~h)1I|-+-+c9B12 zX!uvH#6CSQ$PKVS_{I}r5hm7Q0#7o(RB&HX4nLFWSd+GAUG3w z?&-l7djm_nIj{$M4v4>vbN8^)NDK*@bfA?R;`%C&@!fo~$hP#H7j`5vYIwC)@;gy+kh;8D$VsXIlNj~C;h(jucF@;qSyH%#`Uc}9{~RgKhiexGs;zoz`M}J3hW>=J)ijklo#zzA7Te{;YGi z*ZFmggWQ^59&2w|$@iJg`bVP$g z#{b|=Qr}`zyDZnNd6=(;VZOt06@8H@)!F{FmUs4~Ki)#?;sW87f4^pM8ctJ&SN_{; z21((S*^5@Fc@+f&I+j@ZLAd`+ax@?phkP)}>t8Hb0m2BBbr1^;2JP5TpyF+=i$igj zdXa@Opuqy@P&scbc%99^7sfy}Hw*bz6kP-tTN#u-LDiabsfO<4|MQVCpnO9TM5N{q zo?FbYz zK7Zx85tfApgZ@FF?c4%`=Y~2K8VurD;2j$a3|@k8qM@R@1+Gg$#juOB8N49~v?(PN zNP?>3xsMF0SQZ~j)F{v`3G{`THxxzD!E6yeAwg_sffAj$FM?l4kwLrt1+TP0?HvA1 zGw8=6v4A3@2nLK~LY5M%pa@S;1}^@W_6(rg9obV5D__8CSFlUHz*S|W7S;BUwdC17 zGg12;=z!s^*YKwI=V`jtD*QaO*mT&3t*2=;Ecw^5wu_t+dtYL5UW&1{On92T60xV_JjMV%msjax81XThgEw>(ca(chGK;wJ+$#?Llg+89H zqxXdAe4d}6-4S5OAG%(d|H-MUA3b$WD1%3Zm#I~)Qcw9JT{s1mSF^L`V9Uky{fkra zVma9YJY|^%+pKj=9;(w{y`ZPUVxBYfDnQItS%|UobJfe+2~91Hj+u^p9QFBI`CVM_ z))#|6Iy_y+fq(KX(XoH95BVVC_S=5;(c^I~awDlDYz@AQ_S!tM(Y|f^RxK|XGlbj5 zFqWu#YxN-J?0rEit>p(Bq+DOJeYLh6J*%r`D!M7GYvp&$#P6}srpKoyes_J&qcPG6 zs8m5l8f5w3IQ=```^{j7t7*P+vB%jxhV@$+OaC8jZylD^yKR9=3ep|YASKe@M@fT( zU;ttOBHbY^4bmx+5>gVpkg+`oa~9i&4)YnMX<;&1cHgT3?xjVMfdTfG~m$%{DU2A(Wu# zn_jv7VAFel*+DuFIst*5?CPBs9;AqPV)!x^1$8492*Cpc3XTR3utDYx=l z*6NpFji(>pP&KoRcxJ=Z!8O~-sUczEDaAWkzy{QR*S z{$%D>;zIVJnGkQ(Czx}h+v!+jIF2PRmRe&89|nh#Vm-|DAe_#@CmC75j>o-2tuC#>)Bog4ZmP2_6$ znygq4QPSP=CNW>JBQNieN3}z@^>1`Je-UHEP>kFeR0xm7b&CcAIl zq|L&H{HwkJ(KPvkRPP7-l%h0G?m%8}?H^_FE36F41QoLlQ7)4yg$aKtQJc_Fc3)%` zq<#~SRt!@lUaF&+*!dH0*) zJ0Hs(tmGbjp+MJ)qaX)LFTGj1sVX%SV3wCX4z|PwTiQX#7@YLlUe%9X@ zFgFENW#&r0M`3HSKa!EUdK>5dph41|rw@q}i`D7a4Kuryp1p}Q#CG>4{$NN7lTg*c z%?o?>?gqvaRKxh`4HFL9+~M5!_$~$t!IBLn-I>s|!U|`Ej zrkOhWuo;*%vuc+*V5<2Cia*m??9o$vc4*W+qWie>+bd|CIuiZ&QUUfkpTeQL_my2; zj^y1xXqg7y#;G4Lj+keQ2}S3qaB3%ioz}M+t!&mleB}H-?>YT$$fQGbW@s+WeQ$oH zyEli1i+~Gk{!{zRrL96&UsK>M#8()oIE}6Bk*-p*T5heJ`JvsckXyGbTmKxtLo4`| z&MOVGlSHewc?i58c9{BUabC=$G^955ZC0kf=Qov6zsUSR6}-Wh-J4fn-s2(}F7r_` zFu_AZsJ+y|?CGA-9KN1hydROxV{7W7Q^*rEd>-+X9_FCoV#2RKYV(sO^?P3t2^5Z6 zr7FsE_}|^7fhX%*F+)w0%wZ}fS?&uK%tVjPTCqWVCYRd&>26pn8I_>7=Sq+R;>00r zJ+bI#C#KSM(FGi0!5~q*S2*}80WX(YVhLq|HvmSCyBhz?yFnEW`-=^dNfPs@@4h`m zV}q%_8xSjnhL*XB#K&2G5KUsZ9GBa~vK)g#=`Hj6qXhQB@~sLWRTC`8)=qc#*o}4I zK%7F$=}_qDlo`ONLP&d0;%*sQhJ(4$=FGQ*+g zHon~%a`lAU&)J2t&nMn=Z_@JUeBrU~h#g9A?00{|aZ5jN5}rVA@y1*dnkeDju|gzd zWbsI4_zH7(u}PN~#%uUnn*Yc7OVw~$ep{iUMj?2s5 z9u~mriZ~XV>JPBu#M>8WV%|BlAOGBZV_pkQ10v*Ai-p%=L~uS!IN5?u(OE4otnrv> z%QTmHTU+#@6&)UT8E zn=ri`W|+GQvXaN(S3>oVK`XCr@eB)l3kubsHpFI z%VZvg)A3n1wYN*UVZi8?ki%8K&g?}#JvgmWok!tfV%g$-%+*XY8H=V9^#UI@W#F|< z9dU#=zMAM=73NLG;Yr*dO|FC|ZN|567tgjEyqRCgTgqZe%LwB9K;%v#ZTXJtdBc7_ z3oa>@@mN~|4-D7$q%O(zSF|GU(=7r+AS<_corcu$A~Vc$ggiBLw<|=4iYVC|o4Pi} z>H;Sl3%*yC9kB9>+=ICOc!e=$DvB>|-Ep`%H`Xb-=jZozRMJkZ)41Z8a)Y=d-69mC zEfVukClTRbP(;U$UWJ~qN$&I*z4Ga^eeUDqw}*Si90$uNKgu&g%Ua`c>NbjewcC@K z))7sXyd{HQcypizKc-qZThijDZLB#|Ru0fme`2tRk_XFw*z41<*cvb&LFk4cfAWbY zxa{$W=kv!pM%Z4}C<#MZ2YTb5GM13$#n^&O5jQ2M=U6_S$F5i0+bbi*rG8(Yz%c#( z#i!wgmp#j?W9Hd!by-Ic)ZRQlDd;MRNbARDbL`k@VCu#b?7$9wM(X6nNqt`tPW52( zOPB2_YVGz{n#IJME2wyLr1eGLn=7OK%{246 z_2S?F{5q~?WqzxYy#~t(6m9|)u^@r}+u{88E^`7&2B6W<)j?m*F@Ve%(9r;Pry!gB zn|I@NqXxhnAo_rdt_;^dQvZw^kS788m#d@xi_-;EGXqU`z`%cTx;QQhIYA6;epN#M z;h6xcT7awySm4W<0+6-7XcK#pLAg9-ASL$4)dJksfeJaWz<+V9*?|VhK)LFbz!z<% z{}}nA+ap*i=t_nZWX5bju@@_F7%tEF!p7&%fdQ8yC|F|UH~*V!j^>4HE*l`QyBhf~ z9tt)#Ams=4+PspS1)DQa(hOR4b=mj}>}?NJ*0BS5>no%F^&|Js+5G<}m&Eb_DE$YD zn}Ln_TW6r(QhNU+mLNbf@-Ll%DsGPij1=!WREnTIsb+qF%0k6(XYj_Naik`eVfV_< z_(5jaq2wkJ!^XpJSRxDh!OyQ>vLsf$Ex(sH*X?`{%ctgk7

Q_k9K>7_{X*_L(U>mMDDb>47>XS;zZ1LyTc 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

*&=%v6_m>7A3V6KyRxVcq!W(3 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 literal 0 HcmV?d00001 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/ChangeLog.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/ChangeLog.txt new file mode 100644 index 0000000000..5e423324cf --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/ChangeLog.txt @@ -0,0 +1,202 @@ +uthash ChangeLog +================ + +include::sflogo.txt[] + +Click to return to the link:index.html[uthash home page]. + +Version 1.9.6 (2012-04-28) +-------------------------- +* add utarray_prev (thanks, Ben Hiett!) +* add parens/casts for greater compatibility (thanks, Atis, Debasis Ganguly, and Steve McClellan!) +* added ifndef to uthash_malloc and related hooks (thanks, Holger Machens!) +* edit examples so they do not leak memory (thanks, 任晶磊!) + +Version 1.9.5 (2011-11-16) +-------------------------- +* added `utarray_renew` +* fixed memory leak in `uthash_clear` when using Bloom filter (thanks, Jan Hättig!) +* utarray now copies the UT_icd on array creation rather than storing a pointer +* add parentheses to `HASH_ADD` to fix preprocessing of certain arguments (thanks, Aaron Rosen!) +* more parenthesizations for greater macro argument flexibility + +Version 1.9.4 (2011-06-05) +-------------------------- +* uthash now supports MurmurHash v3 +* utlist now includes concatenation macros (`LL_CONCAT` and `DL_CONCAT`) +* utarray now supports binary search (`utarray_find`) +* utstring now supports a new-or-clear-existing macro (`utstring_renew`) +* documented technique for a multi-level hash table +* clarified scope requirements for `UT_icd` in the utarray documentation +* fixed termination when `utstring_clear` is followed by `utstring_body` +* fixed utarray_inserta macro when used with complex arguments +* on Visual Studio define missing type `uint8_t` +* Debian/Ubuntu include uthash in the package `uthash-dev`. +* uthash has been downloaded 16,211 times. + +Thanks to Yu Feng, Richard Cook, Dino Ciuffetti, Chris Groer, and Arun Cherian +for feedback and fixes in this release! + +Version 1.9.3 (2010-10-31) +-------------------------- +* fix an `ifdef` for compatibility with Intel compiler (thanks, degski!) +* fix `HASH_ITER` macro to satisfy C++ casting rules (thanks, Erik Bai!) + +Version 1.9.2 (2010-10-04) +-------------------------- +* new `HASH_ITER` macro for more convenient deletion-safe iteration +* `hashscan` can now run on FreeBSD 8.1 and later (thanks, Markus Gebert!) +* More parens to evaluate complex macro arguments properly (thanks, ngg!) +* Add sz parameter to the `uthash_free` hook for platforms that do their own memory management. Hopefully this minor API change doesn't cause too much breakage for people. (thanks, Niall Douglas!) +* uthash has been downloaded 12,294 times + +Version 1.9.1 (2010-05-15) +-------------------------- +* Fix a redefinition warning when using `uthash.h` and `utstring.h` together +* Fix a bug in `utstring_init` +* Added `HASH_FIND_PTR` and `HASH_ADD_PTR` (thanks, Niall Douglas!) + +Version 1.9 (2010-03-31) +-------------------------- +* uthash now supports Visual Studio 2008 and 2010 in C or C++ code! +* new headers link:utarray.html[utarray.h] and link:utstring.html[utstring.h] + are now included. These implement dynamic arrays and strings using macros +* link:utlist.html[utlist.h] now has deletion-safe iterators and search macros +* the test suite now runs under Visual Studio (thanks again degski!) +* special thanks for suggesting utarray and utlist features to Charalampos P.! +* uthash has been downloaded 9,616 times + +Version 1.8 (2009-09-08) +-------------------------- +* Added the `hashscan` utility that can report on the size and quality of + hash tables in a running process (Linux-only) +* Added Bloom filter support. This has the potential to speed up certain + types of programs that look up non-existant keys in sufficient numbers. +* Restored the MurmurHash, which can once again be used, if an additional + symbol is defined. This is a "safety" by which the user declares they + understand that `-fno-strict-aliasing` flag must be used if they are + using MurmurHash under gcc with optimization. +* Unified the bucket/table malloc hooks; now there is only one malloc hook +* Re-organized the manual into a main section and advanced topics section +* Fixed a bug in `utlist.h` where sorting a singly-linked list threw a + compile-time error. +* Fixed a bug in `utlist.h` where a doubly-linked list that is sorted + did not maintain the special `head->prev` pointer back to the list tail. + +Version 1.7 (2009-06-11) +-------------------------- +* The MurmurHash has been removed, and Jenkin's hash is once again the default. + While MurmurHash performed well, it's unsafe with regard to the strict + aliasing rule. This results in incorrect code when compiled with optimization. + It's not possible to enable `-fno-strict-aliasing` from within a header file. +* The linked list macros in `utlist.h` now comply with the strict-aliasing + rule so they generate correct code under high optimization levels (O2 or O3). + The use of the `__typeof__` extension, which was originally a GNU extension, + may reduce portability to other compilers that do not support this extension. + This extension is used in the singly-linked list macros and the sort macros. + +Version 1.6 (2009-05-08) +-------------------------- +Special thanks to Alfred Heisner for contributing several enhancements: + +* Support for two new hash functions: + - the Paul Hsieh hash function (`HASH_SFH`) + - Austin Appleby's MurmurHash function (`HASH_MUR`) +* Because of its excellent performance, MurmurHash is now the default hash function. +* `keystats` now has much better elapsed time accuracy under Cygwin and MinGW +* fixed casting in `HASH_FNV`, `HASH_SAX` and `HASH_OAT` for non-char keys + +This release also includes: + +* a new `HASH_CLEAR` operation clears a hash table in one step. +* a new `HASH_SELECT` operation inserts those elements from one hash that + satisfy a given condition into another hash. The selected items have + dual presence in both hash tables. For example a game could select the + visible polygons from a hash of all polygons. +* fixed a compile-time error which occurred if the final argument to + `HASH_ADD_KEYPTR` was a pointer to an array member like `&a[i]` +* added another test script `tests/all_funcs` which executes the test suite + using every supported hash function + +And lastly, + +* a new, separate header called link:utlist.html[utlist.h] is included which + provides 'linked list macros' for C structures, similar in style to the + uthash macros + +Version 1.5 (2009-02-19) +-------------------------- +* now thread-safe for concurrent readers +* use scratch variables on stack rather than in table (thanks, Petter Arvidsson!). + This change made HASH_FIND about 13% faster and enabled reader concurrency. +* made link:license.html[BSD license] terms even more permissive +* added link:userguide.pdf[PDF version] of User Guide +* added http://troydhanson.wordpress.com/feed/[update news] image:img/rss.png[(RSS)] + +Version 1.4 (2008-09-23) +-------------------------- +* Add `HASH_COUNT` for counting items in the hash +* Compatibility with C\+\+. Satisfy additional casting requirements. + Also in the `tests/` directory, running `make cplusplus` now compiles + all the test programs with the C++ compiler. +* Eliminate `elmt` pointer from the UT_hash_handle. Calculate elmt + from hash handle address by subtracting `hho` (hash handle offset). +* Contributed by L.S.Chin: + Cast `void*` to char* before pointer arithmetic to suppress compiler + warnings. We assume compilers abide to C standards which impose + requirement that `sizeof(void*) == sizeof(char*)`. +* Return meaningful exit status from do_tests per Tiago Cunha, + so that package manager-based install can verify tests are successful + + +Version 1.3 (2008-07-27) +------------------------ +* use integer-only math-- no floating point! Support FPU-less CPU's. +* eliminate `hash_q` metric, which measured the fraction of items with + non-ideal chain positions. We only need to know if this fraction + is below 0.5. This is now determined using fast bitwise tests. +* when an item is added to the hash, calculate the key's hash value + upfront and store it, instead of recomputing it as needed. This hashv + is stored in the hash handle. Potentially major speed benefit for + bucket expansion algorithm. Deleting is marginally improved too. +* fixed a minor bug in the calculation of the max ideal chain length; + line 446 in v1.2 erroneously calculated a/b*2 instead of a/(b*2). + The effect of this bug was that bucket expansion could occur more + readily because the per-bucket 'max chain length multiplier factor' + (which delays bucket expansion when certain buckets are overused) + was set to a lower, expansion-favoring value than intended. +* improved source commenting and improved variable names in structures +* remove `HASH_JSW`. Lengthy random number array made code less readable +* add `HASH_SRT(hh,hash,cmp)` as a generalized `HASH_SORT(hash,cmp)`. + It was an omission in uthash 1.2 that there was no sort macro for + hash handles with names other than hh. +* Corrected `HASH_FSCK` so it works with any name for the hash handle. +* behave properly in pathological `HASH_DEL(a,a)` case where the same + variable references the head and the deletee (advancing the head + then loses the correct reference to the deletee); fix by using + scratch area in the hash table to store deletee hash handle. +* made tests runnable on MinGW +* 3000+ downloads since uthash-1.0 + + +Version 1.2 (2006-11-22) +------------------------ +* new `HASH_SORT` macro +* Cygwin support +* User Guide now features a clickable Table of Contents. + (The technique for generating the TOC on the browser was contributed + back to the AsciiDoc project and incorporated into AsciiDoc v8.1.0). + + +Version 1.1 (2006-06-28) +------------------------ +* uthash-1.1 released +* supports several built-in user-selectable hash functions +* new keystats utility quantifies performance of hash functions + + +Version 1.0 (2006-06-02) +------------------------ +* Initial release + +// vim: set syntax=asciidoc: diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/sflogo.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/sflogo.txt new file mode 100644 index 0000000000..3eca8c4426 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/sflogo.txt @@ -0,0 +1,5 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +uthash at SourceForge.net ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/toc.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/toc.txt new file mode 100644 index 0000000000..458eed18e2 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/toc.txt @@ -0,0 +1,85 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +

+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav.txt new file mode 100644 index 0000000000..46da28ac1a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav.txt @@ -0,0 +1,10 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ sf.net summary page + >> uthash home + >> User Guide + [View PDF] +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utarray.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utarray.txt new file mode 100644 index 0000000000..591076a91f --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utarray.txt @@ -0,0 +1,8 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ uthash home + >> utarray macros +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utlist.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utlist.txt new file mode 100644 index 0000000000..6acdf7bea1 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utlist.txt @@ -0,0 +1,8 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ uthash home + >> utlist macros +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utstring.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utstring.txt new file mode 100644 index 0000000000..192117b900 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/topnav_utstring.txt @@ -0,0 +1,8 @@ +ifdef::backend-xhtml11[] ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +
+ uthash home + >> utstring macros +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +endif::backend-xhtml11[] diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/userguide.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/userguide.txt new file mode 100644 index 0000000000..3e65a52fc9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/userguide.txt @@ -0,0 +1,1682 @@ +uthash User Guide +================= +Troy D. Hanson +v1.9.6, April 2012 + +include::sflogo.txt[] +include::topnav.txt[] + +A hash in C +----------- +include::toc.txt[] + +This document is written for C programmers. Since you're reading this, chances +are that you know a hash is used for looking up items using a key. In scripting +languages like Perl, hashes are used all the time. In C, hashes don't exist in +the language itself. This software provides a hash table for C structures. + +What can it do? +~~~~~~~~~~~~~~~~~ +This software supports these operations on items in a hash table: + +1. add +2. find +3. delete +4. count +5. iterate +6. sort +7. select + +Is it fast? +~~~~~~~~~~~ +Add, find and delete are normally constant-time operations. This is influenced +by your key domain and the hash function. + +This hash aims to be minimalistic and efficient. It's around 900 lines of C. +It inlines automatically because it's implemented as macros. It's fast as long +as the hash function is suited to your keys. You can use the default hash +function, or easily compare performance and choose from among several other +<>. + +Is it a library? +~~~~~~~~~~~~~~~~ +No, it's just a single header file: `uthash.h`. All you need to do is copy +the header file into your project, and: + + #include "uthash.h" + +Since uthash is a header file only, there is no library code to link against. + +C/C++ and platforms +~~~~~~~~~~~~~~~~~~~ +This software can be used in C and C++ programs. It has been tested on: + + * Linux + * Mac OS X + * Windows using Visual Studio 2008 and 2010 + * Solaris + * OpenBSD + * FreeBSD + +Test suite +^^^^^^^^^^ +To run the test suite, enter the `tests` directory. Then, + + * on Unix platforms, run `make` + * on Windows, run the "do_tests_win32.cmd" batch file. (You may edit the + batch file if your Visual Studio is installed in a non-standard location). + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Obtaining uthash +~~~~~~~~~~~~~~~~ +Please follow the link to download on the +http://uthash.sourceforge.net[uthash website] at http://uthash.sourceforge.net. + +A number of platforms include uthash in their package repositories. For example, +Debian/Ubuntu users may simply `aptitude install uthash-dev`. + +Getting help +~~~~~~~~~~~~ +Feel free to mailto:tdh@tkhanson.net[email the author] at +tdh@tkhanson.net. + +Resources +~~~~~~~~~ +Users of uthash may wish to follow the news feed for information about new +releases. Also, there are some extra bonus headers included with uthash. + +News:: + The author has a news feed for http://troydhanson.wordpress.com/[software updates] image:img/rss.png[(RSS)]. +Extras included with uthash:: + uthash ships with these "extras"-- independent headers similar to uthash. + First link:utlist.html[utlist.h] provides linked list macros for C structures. + Second, link:utarray.html[utarray.h] implements dynamic arrays using macros. + Third, link:utstring.html[utstring.h] implements a basic dynamic string. +Other software:: + Other open-source software by the author is listed at http://tkhanson.net. + +Who's using it? +~~~~~~~~~~~~~~~ +Since releasing uthash in 2006, it has been downloaded thousands of times, +incorporated into commercial software, academic research, and into other +open-source software. + +Your structure +-------------- + +In uthash, a hash table is comprised of structures. Each structure represents a +key-value association. One or more of the structure fields constitute the key. +The structure pointer itself is the value. + +.Defining a structure that can be hashed +---------------------------------------------------------------------- +#include "uthash.h" + +struct my_struct { + int id; /* key */ + char name[10]; + UT_hash_handle hh; /* makes this structure hashable */ +}; +---------------------------------------------------------------------- + +Note that, in uthash, your structure will never be moved or copied into another +location when you add it into a hash table. This means that you can keep other +data structures that safely point to your structure-- regardless of whether you +add or delete it from a hash table during your program's lifetime. + +The key +~~~~~~~ +There are no restrictions on the data type or name of the key field. The key +can also comprise multiple contiguous fields, having any names and data types. + +.Any data type... really? +***************************************************************************** +Yes, your key and structure can have any data type. Unlike function calls with +fixed prototypes, uthash consists of macros-- whose arguments are untyped-- and +thus able to work with any type of structure or key. +***************************************************************************** + +Unique keys +^^^^^^^^^^^ +As with any hash, every item must have a unique key. Your application must +enforce key uniqueness. Before you add an item to the hash table, you must +first know (if in doubt, check!) that the key is not already in use. You +can check whether a key already exists in the hash table using `HASH_FIND`. + +The hash handle +~~~~~~~~~~~~~~~ +The `UT_hash_handle` field must be present in your structure. It is used for +the internal bookkeeping that makes the hash work. It does not require +initialization. It can be named anything, but you can simplify matters by +naming it `hh`. This allows you to use the easier "convenience" macros to add, +find and delete items. + +A word about memory +~~~~~~~~~~~~~~~~~~~ +Some have asked how uthash cleans up its internal memory. The answer is simple: +'when you delete the final item' from a hash table, uthash releases all the +internal memory associated with that hash table, and sets its pointer to NULL. + +Hash operations +--------------- + +This section introduces the uthash macros by example. For a more succinct +listing, see <>. + +.Convenience vs. general macros: +***************************************************************************** +The uthash macros fall into two categories. The 'convenience' macros can be used +with integer, pointer or string keys (and require that you chose the conventional +name `hh` for the `UT_hash_handle` field). The convenience macros take fewer +arguments than the general macros, making their usage a bit simpler for these +common types of keys. + +The 'general' macros can be used for any types of keys, or for multi-field keys, +or when the `UT_hash_handle` has been named something other than `hh`. These +macros take more arguments and offer greater flexibility in return. But if the +convenience macros suit your needs, use them-- your code will be more readable. +***************************************************************************** + +Declare the hash +~~~~~~~~~~~~~~~~ +Your hash must be declared as a `NULL`-initialized pointer to your structure. + + struct my_struct *users = NULL; /* important! initialize to NULL */ + +Add item +~~~~~~~~ +Allocate and initialize your structure as you see fit. The only aspect +of this that matters to uthash is that your key must be initialized to +a unique value. Then call `HASH_ADD`. (Here we use the convenience macro +`HASH_ADD_INT`, which offers simplified usage for keys of type `int`). + +.Add an item to a hash +---------------------------------------------------------------------- +void add_user(int user_id, char *name) { + struct my_struct *s; + + s = malloc(sizeof(struct my_struct)); + s->id = user_id; + strcpy(s->name, name); + HASH_ADD_INT( users, id, s ); /* id: name of key field */ +} +---------------------------------------------------------------------- + +The first parameter to `HASH_ADD_INT` is the hash table, and the +second parameter is the 'name' of the key field. Here, this is `id`. The +last parameter is a pointer to the structure being added. + +[[validc]] +.Wait.. the field name is a parameter? +******************************************************************************* +If you find it strange that `id`, which is the 'name of a field' in the +structure, can be passed as a parameter, welcome to the world of macros. Don't +worry- the C preprocessor expands this to valid C code. +******************************************************************************* + +Key must not be modified while in-use +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Once a structure has been added to the hash, do not change the value of its key. +Instead, delete the item from the hash, change the key, and then re-add it. + +Passing the hash pointer into functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the example above `users` is a global variable, but what if the caller wanted +to pass the hash pointer 'into' the `add_user` function? At first glance it would +appear that you could simply pass `users` as an argument, but that won't work +right. + + /* bad */ + void add_user(struct my_struct *users, int user_id, char *name) { + ... + HASH_ADD_INT( users, id, s ); + } + +You really need to pass 'a pointer' to the hash pointer: + + /* good */ + void add_user(struct my_struct **users, int user_id, char *name) { ... + ... + HASH_ADD_INT( *users, id, s ); + } + +Note that we dereferenced the pointer in the `HASH_ADD` also. + +The reason it's necessary to deal with a pointer to the hash pointer is simple: +the hash macros modify it (in other words, they modify the 'pointer itself' not +just what it points to). + +Find item +~~~~~~~~~ +To look up a structure in a hash, you need its key. Then call `HASH_FIND`. +(Here we use the convenience macro `HASH_FIND_INT` for keys of type `int`). + +.Find a structure using its key +---------------------------------------------------------------------- +struct my_struct *find_user(int user_id) { + struct my_struct *s; + + HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */ + return s; +} +---------------------------------------------------------------------- + +Here, the hash table is `users`, and `&user_id` points to the key (an integer +in this case). Last, `s` is the 'output' variable of `HASH_FIND_INT`. The +final result is that `s` points to the structure with the given key, or +is `NULL` if the key wasn't found in the hash. + +[NOTE] +The middle argument is a 'pointer' to the key. You can't pass a literal key +value to `HASH_FIND`. Instead assign the literal value to a variable, and pass +a pointer to the variable. + + +Delete item +~~~~~~~~~~~ +To delete a structure from a hash, you must have a pointer to it. (If you only +have the key, first do a `HASH_FIND` to get the structure pointer). + +.Delete an item from a hash +---------------------------------------------------------------------- +void delete_user(struct my_struct *user) { + HASH_DEL( users, user); /* user: pointer to deletee */ + free(user); /* optional; it's up to you! */ +} +---------------------------------------------------------------------- + +Here again, `users` is the hash table, and `user` is a pointer to the +structure we want to remove from the hash. + +uthash never frees your structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Deleting a structure just removes it from the hash table-- it doesn't `free` +it. The choice of when to free your structure is entirely up to you; uthash +will never free your structure. + +Delete can change the pointer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The hash table pointer (which initially points to the first item added to the +hash) can change in response to `HASH_DEL` (i.e. if you delete the first item +in the hash table). + +Iterative deletion +^^^^^^^^^^^^^^^^^^ +The `HASH_ITER` macro is a deletion-safe iteration construct which expands +to a simple 'for' loop. + +.Delete all items from a hash +---------------------------------------------------------------------- +void delete_all() { + struct my_struct *current_user, *tmp; + + HASH_ITER(hh, users, current_user, tmp) { + HASH_DEL(users,current_user); /* delete; users advances to next */ + free(current_user); /* optional- if you want to free */ + } +} +---------------------------------------------------------------------- + +All-at-once deletion +^^^^^^^^^^^^^^^^^^^^ +If you only want to delete all the items, but not free them or do any +per-element clean up, you can do this more efficiently in a single operation: + + HASH_CLEAR(hh,users); + +Afterward, the list head (here, `users`) will be set to `NULL`. + +Count items +~~~~~~~~~~~ + +The number of items in the hash table can be obtained using `HASH_COUNT`: + +.Count of items in the hash table +---------------------------------------------------------------------- +unsigned int num_users; +num_users = HASH_COUNT(users); +printf("there are %u users\n", num_users); +---------------------------------------------------------------------- + +Incidentally, this works even the list (`users`, here) is `NULL`, in +which case the count is 0. + +Iterating and sorting +~~~~~~~~~~~~~~~~~~~~~ + +You can loop over the items in the hash by starting from the beginning and +following the `hh.next` pointer. + +.Iterating over all the items in a hash +---------------------------------------------------------------------- +void print_users() { + struct my_struct *s; + + for(s=users; s != NULL; s=s->hh.next) { + printf("user id %d: name %s\n", s->id, s->name); + } +} +---------------------------------------------------------------------- + +There is also an `hh.prev` pointer you could use to iterate backwards through +the hash, starting from any known item. + +[[deletesafe]] +Deletion-safe iteration +^^^^^^^^^^^^^^^^^^^^^^^ +In the example above, it would not be safe to delete and free `s` in the body +of the 'for' loop, (because `s` is derefenced each time the loop iterates). +This is easy to rewrite correctly (by copying the `s->hh.next` pointer to a +temporary variable 'before' freeing `s`), but it comes up often enough that a +deletion-safe iteration macro, `HASH_ITER`, is included. It expands to a +`for`-loop header. Here is how it could be used to rewrite the last example: + + struct my_struct *s, *tmp; + + HASH_ITER(hh, users, s, tmp) { + printf("user id %d: name %s\n", s->id, s->name); + /* ... it is safe to delete and free s here */ + } + +.A hash is also a doubly-linked list. +******************************************************************************* +Iterating backward and forward through the items in the hash is possible +because of the `hh.prev` and `hh.next` fields. All the items in the hash can +be reached by repeatedly following these pointers, thus the hash is also a +doubly-linked list. +******************************************************************************* + +If you're using uthash in a C++ program, you need an extra cast on the `for` +iterator, e.g., `s=(struct my_struct*)s->hh.next`. + +Sorted iteration +^^^^^^^^^^^^^^^^ +The items in the hash are, by default, traversed in the order they were added +("insertion order") when you follow the `hh.next` pointer. But you can sort +the items into a new order using `HASH_SORT`. E.g., + + HASH_SORT( users, name_sort ); + +The second argument is a pointer to a comparison function. It must accept two +arguments which are pointers to two items to compare. Its return value should +be less than zero, zero, or greater than zero, if the first item sorts before, +equal to, or after the second item, respectively. (Just like `strcmp`). + +.Sorting the items in the hash +---------------------------------------------------------------------- +int name_sort(struct my_struct *a, struct my_struct *b) { + return strcmp(a->name,b->name); +} + +int id_sort(struct my_struct *a, struct my_struct *b) { + return (a->id - b->id); +} + +void sort_by_name() { + HASH_SORT(users, name_sort); +} + +void sort_by_id() { + HASH_SORT(users, id_sort); +} +---------------------------------------------------------------------- + +When the items in the hash are sorted, the first item may change position. In +the example above, `users` may point to a different structure after calling +`HASH_SORT`. + +A complete example +~~~~~~~~~~~~~~~~~~ + +We'll repeat all the code and embellish it with a `main()` function to form a +working example. + +If this code was placed in a file called `example.c` in the same directory as +`uthash.h`, it could be compiled and run like this: + + cc -o example example.c + ./example + +Follow the prompts to try the program. + +.A complete program +---------------------------------------------------------------------- +#include /* gets */ +#include /* atoi, malloc */ +#include /* strcpy */ +#include "uthash.h" + +struct my_struct { + int id; /* key */ + char name[10]; + UT_hash_handle hh; /* makes this structure hashable */ +}; + +struct my_struct *users = NULL; + +void add_user(int user_id, char *name) { + struct my_struct *s; + + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + s->id = user_id; + strcpy(s->name, name); + HASH_ADD_INT( users, id, s ); /* id: name of key field */ +} + +struct my_struct *find_user(int user_id) { + struct my_struct *s; + + HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */ + return s; +} + +void delete_user(struct my_struct *user) { + HASH_DEL( users, user); /* user: pointer to deletee */ + free(user); +} + +void delete_all() { + struct my_struct *current_user, *tmp; + + HASH_ITER(hh, users, current_user, tmp) { + HASH_DEL(users,current_user); /* delete it (users advances to next) */ + free(current_user); /* free it */ + } +} + +void print_users() { + struct my_struct *s; + + for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) { + printf("user id %d: name %s\n", s->id, s->name); + } +} + +int name_sort(struct my_struct *a, struct my_struct *b) { + return strcmp(a->name,b->name); +} + +int id_sort(struct my_struct *a, struct my_struct *b) { + return (a->id - b->id); +} + +void sort_by_name() { + HASH_SORT(users, name_sort); +} + +void sort_by_id() { + HASH_SORT(users, id_sort); +} + +int main(int argc, char *argv[]) { + char in[10]; + int id=1, running=1; + struct my_struct *s; + unsigned num_users; + + while (running) { + printf("1. add user\n"); + printf("2. find user\n"); + printf("3. delete user\n"); + printf("4. delete all users\n"); + printf("5. sort items by name\n"); + printf("6. sort items by id\n"); + printf("7. print users\n"); + printf("8. count users\n"); + printf("9. quit\n"); + gets(in); + switch(atoi(in)) { + case 1: + printf("name?\n"); + add_user(id++, gets(in)); + break; + case 2: + printf("id?\n"); + s = find_user(atoi(gets(in))); + printf("user: %s\n", s ? s->name : "unknown"); + break; + case 3: + printf("id?\n"); + s = find_user(atoi(gets(in))); + if (s) delete_user(s); + else printf("id unknown\n"); + break; + case 4: + delete_all(); + break; + case 5: + sort_by_name(); + break; + case 6: + sort_by_id(); + break; + case 7: + print_users(); + break; + case 8: + num_users=HASH_COUNT(users); + printf("there are %u users\n", num_users); + break; + case 9: + running=0; + break; + } + } + + delete_all(); /* free any structures */ + return 0; +} +---------------------------------------------------------------------- + +This program is included in the distribution in `tests/example.c`. You can run +`make example` in that directory to compile it easily. + +Standard key types +------------------ +This section goes into specifics of how to work with different kinds of keys. +You can use nearly any type of key-- integers, strings, pointers, structures, etc. + +[NOTE] +.A note about float +================================================================================ +You can use floating point keys. This comes with the same caveats as with any +program that tests floating point equality. In other words, even the tiniest +difference in two floating point numbers makes them distinct keys. +================================================================================ + +Integer keys +~~~~~~~~~~~~ +The preceding examples demonstrated use of integer keys. To recap, use the +convenience macros `HASH_ADD_INT` and `HASH_FIND_INT` for structures with +integer keys. (The other operations such as `HASH_DELETE` and `HASH_SORT` are +the same for all types of keys). + +String keys +~~~~~~~~~~~ +If your structure has a string key, the operations to use depend on whether your +structure 'points to' the key (`char *`) or the string resides `within` the +structure (`char a[10]`). *This distinction is important*. As we'll see below, +you need to use `HASH_ADD_KEYPTR` when your structure 'points' to a key (that is, +the key itself is 'outside' of the structure); in contrast, use `HASH_ADD_STR` +for a string key that is contained *within* your structure. + +[NOTE] +.char[ ] vs. char* +================================================================================ +The string is 'within' the structure in the first example below-- `name` is a +`char[10]` field. In the second example, the key is 'outside' of the +structure-- `name` is a `char *`. So the first example uses `HASH_ADD_STR` but +the second example uses `HASH_ADD_KEYPTR`. For information on this macro, see +the <>. +================================================================================ + +String 'within' structure +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.A string-keyed hash (string within structure) +---------------------------------------------------------------------- +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + char name[10]; /* key (string is WITHIN the structure) */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char **n, *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + int i=0; + + for (n = names; *n != NULL; n++) { + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + strncpy(s->name, *n,10); + s->id = i++; + HASH_ADD_STR( users, name, s ); + } + + HASH_FIND_STR( users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + /* free the hash table contents */ + HASH_ITER(hh, users, s, tmp) { + HASH_DEL(users, s); + free(s); + } + return 0; +} +---------------------------------------------------------------------- + +This example is included in the distribution in `tests/test15.c`. It prints: + + betty's id is 2 + +String 'pointer' in structure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Now, here is the same example but using a `char *` key instead of `char [ ]`: + +.A string-keyed hash (structure points to string) +---------------------------------------------------------------------- +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + const char *name; /* key */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char **n, *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + int i=0; + + for (n = names; *n != NULL; n++) { + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + s->name = *n; + s->id = i++; + HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s ); + } + + HASH_FIND_STR( users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + /* free the hash table contents */ + HASH_ITER(hh, users, s, tmp) { + HASH_DEL(users, s); + free(s); + } + return 0; +} +---------------------------------------------------------------------- + +This example is included in `tests/test40.c`. + +Pointer keys +~~~~~~~~~~~~ +Your key can be a pointer. To be very clear, this means the 'pointer itself' +can be the key (in contrast, if the thing 'pointed to' is the key, this is a +different use case handled by `HASH_ADD_KEYPTR`). + +Here is a simple example where a structure has a pointer member, called `key`. + +.A pointer key +---------------------------------------------------------------------- +#include +#include +#include "uthash.h" + +typedef struct { + void *key; + int i; + UT_hash_handle hh; +} el_t; + +el_t *hash = NULL; +char *someaddr = NULL; + +int main() { + el_t *d; + el_t *e = (el_t*)malloc(sizeof(el_t)); + if (!e) return -1; + e->key = (void*)someaddr; + e->i = 1; + HASH_ADD_PTR(hash,key,e); + HASH_FIND_PTR(hash, &someaddr, d); + if (d) printf("found\n"); + + /* release memory */ + HASH_DEL(hash,e); + free(e); + return 0; +} +---------------------------------------------------------------------- + +This example is included in `tests/test57.c`. Note that the end of the program +deletes the element out of the hash, (and since no more elements remain in the +hash), uthash releases its internal memory. + +Structure keys +~~~~~~~~~~~~~~ +Your key field can have any data type. To uthash, it is just a sequence of +bytes. Therefore, even a nested structure can be used as a key. We'll use the +general macros `HASH_ADD` and `HASH_FIND` to demonstrate. + +NOTE: Structures contain padding (wasted internal space used to fulfill +alignment requirements for the members of the structure). These padding bytes +'must be zeroed' before adding an item to the hash or looking up an item. +Therefore always zero the whole structure before setting the members of +interest. The example below does this-- see the two calls to `memset`. + +.A key which is a structure +---------------------------------------------------------------------- +#include +#include +#include "uthash.h" + +typedef struct { + char a; + int b; +} record_key_t; + +typedef struct { + record_key_t key; + /* ... other data ... */ + UT_hash_handle hh; +} record_t; + +int main(int argc, char *argv[]) { + record_t l, *p, *r, *tmp, *records = NULL; + + r = (record_t*)malloc( sizeof(record_t) ); + memset(r, 0, sizeof(record_t)); + r->key.a = 'a'; + r->key.b = 1; + HASH_ADD(hh, records, key, sizeof(record_key_t), r); + + memset(&l, 0, sizeof(record_t)); + l.key.a = 'a'; + l.key.b = 1; + HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p); + + if (p) printf("found %c %d\n", p->key.a, p->key.b); + + HASH_ITER(hh, records, p, tmp) { + HASH_DEL(records, p); + free(p); + } + return 0; +} + +---------------------------------------------------------------------- + +This usage is nearly the same as use of a compound key explained below. + +Note that the general macros require the name of the `UT_hash_handle` to be +passed as the first argument (here, this is `hh`). The general macros are +documented in <>. + +Advanced Topics +--------------- + +Compound keys +~~~~~~~~~~~~~ +Your key can even comprise multiple contiguous fields. + +.A multi-field key +---------------------------------------------------------------------- +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* memset */ +#include "uthash.h" + +#define UTF32 1 + +typedef struct { + UT_hash_handle hh; + int len; + char encoding; /* these two fields */ + int text[]; /* comprise the key */ +} msg_t; + +typedef struct { + char encoding; + int text[]; +} lookup_key_t; + +int main(int argc, char *argv[]) { + unsigned keylen; + msg_t *msg, *tmp, *msgs = NULL; + lookup_key_t *lookup_key; + + int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 北京 */ + + /* allocate and initialize our structure */ + msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) ); + memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */ + msg->len = sizeof(beijing); + msg->encoding = UTF32; + memcpy(msg->text, beijing, sizeof(beijing)); + + /* calculate the key length including padding, using formula */ + keylen = offsetof(msg_t, text) /* offset of last key field */ + + sizeof(beijing) /* size of last key field */ + - offsetof(msg_t, encoding); /* offset of first key field */ + + /* add our structure to the hash table */ + HASH_ADD( hh, msgs, encoding, keylen, msg); + + /* look it up to prove that it worked :-) */ + msg=NULL; + + lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing)); + memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing)); + lookup_key->encoding = UTF32; + memcpy(lookup_key->text, beijing, sizeof(beijing)); + HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg ); + if (msg) printf("found \n"); + free(lookup_key); + + HASH_ITER(hh, msgs, msg, tmp) { + HASH_DEL(msgs, msg); + free(msg); + } + return 0; +} +---------------------------------------------------------------------- + +This example is included in the distribution in `tests/test22.c`. + +If you use multi-field keys, recognize that the compiler pads adjacent fields +(by inserting unused space between them) in order to fulfill the alignment +requirement of each field. For example a structure containing a `char` followed +by an `int` will normally have 3 "wasted" bytes of padding after the char, in +order to make the `int` field start on a multiple-of-4 address (4 is the length +of the int). + +.Calculating the length of a multi-field key: +******************************************************************************* +To determine the key length when using a multi-field key, you must include any +intervening structure padding the compiler adds for alignment purposes. + +An easy way to calculate the key length is to use the `offsetof` macro from +``. The formula is: + + key length = offsetof(last_key_field) + + sizeof(last_key_field) + - offsetof(first_key_field) + +In the example above, the `keylen` variable is set using this formula. +******************************************************************************* + +When dealing with a multi-field key, you must zero-fill your structure before +`HASH_ADD`'ing it to a hash table, or using its fields in a `HASH_FIND` key. + +In the previous example, `memset` is used to initialize the structure by +zero-filling it. This zeroes out any padding between the key fields. If we +didn't zero-fill the structure, this padding would contain random values. The +random values would lead to `HASH_FIND` failures; as two "identical" keys will +appear to mismatch if there are any differences within their padding. + +[[multilevel]] +Multi-level hash tables +~~~~~~~~~~~~~~~~~~~~~~~ +A multi-level hash table arises when each element of a hash table contains its +own secondary hash table. There can be any number of levels. In a scripting +language you might see: + + $items{bob}{age}=37 + +The C program below builds this example in uthash: the hash table is called +`items`. It contains one element (`bob`) whose own hash table contains one +element (`age`) with value 37. No special functions are necessary to build +a multi-level hash table. + +While this example represents both levels (`bob` and `age`) using the same +structure, it would also be fine to use two different structure definitions. +It would also be fine if there were three or more levels instead of two. + +.Multi-level hash table +---------------------------------------------------------------------- +#include +#include +#include +#include "uthash.h" + +/* hash of hashes */ +typedef struct item { + char name[10]; + struct item *sub; + int val; + UT_hash_handle hh; +} item_t; + +item_t *items=NULL; + +int main(int argc, char *argvp[]) { + item_t *item1, *item2, *tmp1, *tmp2; + + /* make initial element */ + item_t *i = malloc(sizeof(*i)); + strcpy(i->name, "bob"); + i->sub = NULL; + i->val = 0; + HASH_ADD_STR(items, name, i); + + /* add a sub hash table off this element */ + item_t *s = malloc(sizeof(*s)); + strcpy(s->name, "age"); + s->sub = NULL; + s->val = 37; + HASH_ADD_STR(i->sub, name, s); + + /* iterate over hash elements */ + HASH_ITER(hh, items, item1, tmp1) { + HASH_ITER(hh, item1->sub, item2, tmp2) { + printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val); + } + } + + /* clean up both hash tables */ + HASH_ITER(hh, items, item1, tmp1) { + HASH_ITER(hh, item1->sub, item2, tmp2) { + HASH_DEL(item1->sub, item2); + free(item2); + } + HASH_DEL(items, item1); + free(item1); + } + + return 0; +} +---------------------------------------------------------------------- +The example above is included in `tests/test59.c`. + +[[multihash]] +Items in several hash tables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A structure can be added to more than one hash table. A few reasons you might do +this include: + +- each hash table may use an alternative key; +- each hash table may have its own sort order; +- or you might simply use multiple hash tables for grouping purposes. E.g., + you could have users in an `admin_users` and a `users` hash table. + +Your structure needs to have a `UT_hash_handle` field for each hash table to +which it might be added. You can name them anything. E.g., + + UT_hash_handle hh1, hh2; + +Items with alternative keys +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You might create a hash table keyed on an ID field, and another hash table keyed +on username (if usernames are unique). You can add the same user structure to +both hash tables (without duplication of the structure), allowing lookup of a +user structure by their name or ID. The way to achieve this is to have a +separate `UT_hash_handle` for each hash to which the structure may be added. + +.A structure with two alternative keys +---------------------------------------------------------------------- +struct my_struct { + int id; /* usual key */ + char username[10]; /* alternative key */ + UT_hash_handle hh1; /* handle for first hash table */ + UT_hash_handle hh2; /* handle for second hash table */ +}; +---------------------------------------------------------------------- + +In the example above, the structure can now be added to two separate hash +tables. In one hash, `id` is its key, while in the other hash, `username` is +its key. (There is no requirement that the two hashes have different key +fields. They could both use the same key, such as `id`). + +Notice the structure has two hash handles (`hh1` and `hh2`). In the code +below, notice that each hash handle is used exclusively with a particular hash +table. (`hh1` is always used with the `users_by_id` hash, while `hh2` is +always used with the `users_by_name` hash table). + +.Two keys on a structure +---------------------------------------------------------------------- + struct my_struct *users_by_id = NULL, *users_by_name = NULL, *s; + int i; + char *name; + + s = malloc(sizeof(struct my_struct)); + s->id = 1; + strcpy(s->username, "thanson"); + + /* add the structure to both hash tables */ + HASH_ADD(hh1, users_by_id, id, sizeof(int), s); + HASH_ADD(hh2, users_by_name, username, strlen(s->username), s); + + /* lookup user by ID in the "users_by_id" hash table */ + i=1; + HASH_FIND(hh1, users_by_id, &i, sizeof(int), s); + if (s) printf("found id %d: %s\n", i, s->username); + + /* lookup user by username in the "users_by_name" hash table */ + name = "thanson"; + HASH_FIND(hh2, users_by_name, name, strlen(name), s); + if (s) printf("found user %s: %d\n", name, s->id); +---------------------------------------------------------------------- + + +Several sort orders +~~~~~~~~~~~~~~~~~~~ +It comes as no suprise that two hash tables can have different sort orders, but +this fact can also be used advantageously to sort the 'same items' in several +ways. This is based on the ability to store a structure in several hash tables. + +Extending the previous example, suppose we have many users. We have added each +user structure to the `users_by_id` hash table and the `users_by_name` hash table. +(To reiterate, this is done without the need to have two copies of each structure). +Now we can define two sort functions, then use `HASH_SRT`. + + int sort_by_id(struct my_struct *a, struct my_struct *b) { + if (a->id == b->id) return 0; + return (a->id < b->id) ? -1 : 1; + } + + int sort_by_name(struct my_struct *a, struct my_struct *b) { + return strcmp(a->username,b->username); + } + + HASH_SRT(hh1, users_by_id, sort_by_id); + HASH_SRT(hh2, users_by_name, sort_by_name); + +Now iterating over the items in `users_by_id` will traverse them in id-order +while, naturally, iterating over `users_by_name` will traverse them in +name-order. The items are fully forward-and-backward linked in each order. +So even for one set of users, we might store them in two hash tables to provide +easy iteration in two different sort orders. + +Bloom filter (faster misses) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Programs that generate a fair miss rate (`HASH_FIND` that result in `NULL`) may +benefit from the built-in Bloom filter support. This is disabled by default, +because programs that generate only hits would incur a slight penalty from it. +Also, programs that do deletes should not use the Bloom filter. While the +program would operate correctly, deletes diminish the benefit of the filter. +To enable the Bloom filter, simply compile with `-DHASH_BLOOM=n` like: + + -DHASH_BLOOM=27 + +where the number can be any value up to 32 which determines the amount of memory +used by the filter, as shown below. Using more memory makes the filter more +accurate and has the potential to speed up your program by making misses bail +out faster. + +.Bloom filter sizes for selected values of n +[width="50%",cols="10m,30",grid="none",options="header"] +|===================================================================== +| n | Bloom filter size (per hash table) +| 16 | 8 kilobytes +| 20 | 128 kilobytes +| 24 | 2 megabytes +| 28 | 32 megabytes +| 32 | 512 megabytes +|===================================================================== + +Bloom filters are only a performance feature; they do not change the results of +hash operations in any way. The only way to gauge whether or not a Bloom filter +is right for your program is to test it. Reasonable values for the size of the +Bloom filter are 16-32 bits. + +Select +~~~~~~ +An experimental 'select' operation is provided that inserts those items from a +source hash that satisfy a given condition into a destination hash. This +insertion is done with somewhat more efficiency than if this were using +`HASH_ADD`, namely because the hash function is not recalculated for keys of the +selected items. This operation does not remove any items from the source hash. +Rather the selected items obtain dual presence in both hashes. The destination +hash may already have items in it; the selected items are added to it. In order +for a structure to be usable with `HASH_SELECT`, it must have two or more hash +handles. (As described <>, a structure can exist in many +hash tables at the same time; it must have a separate hash handle for each one). + + user_t *users=NULL, *admins=NULL; /* two hash tables */ + + typedef struct { + int id; + UT_hash_handle hh; /* handle for users hash */ + UT_hash_handle ah; /* handle for admins hash */ + } user_t; + +Now suppose we have added some users, and want to select just the administrator +users who have id's less than 1024. + + #define is_admin(x) (((user_t*)x)->id < 1024) + HASH_SELECT(ah,admins,hh,users,is_admin); + +The first two parameters are the 'destination' hash handle and hash table, the +second two parameters are the 'source' hash handle and hash table, and the last +parameter is the 'select condition'. Here we used a macro `is_admin()` but we +could just as well have used a function. + + int is_admin(void *userv) { + user_t *user = (user_t*)userv; + return (user->id < 1024) ? 1 : 0; + } + +If the select condition always evaluates to true, this operation is +essentially a 'merge' of the source hash into the destination hash. Of course, +the source hash remains unchanged under any use of `HASH_SELECT`. It only adds +items to the destination hash selectively. + +The two hash handles must differ. An example of using `HASH_SELECT` is included +in `tests/test36.c`. + + +[[hash_functions]] +Built-in hash functions +~~~~~~~~~~~~~~~~~~~~~~~ +Internally, a hash function transforms a key into a bucket number. You don't +have to take any action to use the default hash function, currently Jenkin's. + +Some programs may benefit from using another of the built-in hash functions. +There is a simple analysis utility included with uthash to help you determine +if another hash function will give you better performance. + +You can use a different hash function by compiling your program with +`-DHASH_FUNCTION=HASH_xyz` where `xyz` is one of the symbolic names listed +below. E.g., + + cc -DHASH_FUNCTION=HASH_BER -o program program.c + +.Built-in hash functions +[width="50%",cols="^5m,20",grid="none",options="header"] +|=============================================================================== +|Symbol | Name +|JEN | Jenkins (default) +|BER | Bernstein +|SAX | Shift-Add-Xor +|OAT | One-at-a-time +|FNV | Fowler/Noll/Vo +|SFH | Paul Hsieh +|MUR | MurmurHash v3 (see note) +|=============================================================================== + +[NOTE] +.MurmurHash +================================================================================ +A special symbol must be defined if you intend to use MurmurHash. To use it, add +`-DHASH_USING_NO_STRICT_ALIASING` to your `CFLAGS`. And, if you are using +the gcc compiler with optimization, add `-fno-strict-aliasing` to your `CFLAGS`. +================================================================================ + +Which hash function is best? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You can easily determine the best hash function for your key domain. To do so, +you'll need to run your program once in a data-collection pass, and then run +the collected data through an included analysis utility. + +First you must build the analysis utility. From the top-level directory, + + cd tests/ + make + +We'll use `test14.c` to demonstrate the data-collection and analysis steps +(here using `sh` syntax to redirect file descriptor 3 to a file): + +.Using keystats +-------------------------------------------------------------------------------- +% cc -DHASH_EMIT_KEYS=3 -I../src -o test14 test14.c +% ./test14 3>test14.keys +% ./keystats test14.keys +fcn ideal% #items #buckets dup% fl add_usec find_usec del-all usec +--- ------ ---------- ---------- ----- -- ---------- ---------- ------------ +SFH 91.6% 1219 256 0% ok 92 131 25 +FNV 90.3% 1219 512 0% ok 107 97 31 +SAX 88.7% 1219 512 0% ok 111 109 32 +OAT 87.2% 1219 256 0% ok 99 138 26 +JEN 86.7% 1219 256 0% ok 87 130 27 +BER 86.2% 1219 256 0% ok 121 129 27 +-------------------------------------------------------------------------------- + +[NOTE] +The number 3 in `-DHASH_EMIT_KEYS=3` is a file descriptor. Any file descriptor +that your program doesn't use for its own purposes can be used instead of 3. +The data-collection mode enabled by `-DHASH_EMIT_KEYS=x` should not be used in +production code. + +Usually, you should just pick the first hash function that is listed. Here, this +is `SFH`. This is the function that provides the most even distribution for +your keys. If several have the same `ideal%`, then choose the fastest one +according to the `find_usec` column. + +keystats column reference +^^^^^^^^^^^^^^^^^^^^^^^^^ +fcn:: + symbolic name of hash function +ideal%:: + The percentage of items in the hash table which can be looked up within an + ideal number of steps. (Further explained below). +#items:: + the number of keys that were read in from the emitted key file +#buckets:: + the number of buckets in the hash after all the keys were added +dup%:: + the percent of duplicate keys encountered in the emitted key file. + Duplicates keys are filtered out to maintain key uniqueness. (Duplicates + are normal. For example, if the application adds an item to a hash, + deletes it, then re-adds it, the key is written twice to the emitted file.) +flags:: + this is either `ok`, or `nx` (noexpand) if the expansion inhibited flag is + set, described in <>. It is not recommended + to use a hash function that has the `noexpand` flag set. +add_usec:: + the clock time in microseconds required to add all the keys to a hash +find_usec:: + the clock time in microseconds required to look up every key in the hash +del-all usec:: + the clock time in microseconds required to delete every item in the hash + +[[ideal]] +ideal% +^^^^^^ + +.What is ideal%? +***************************************************************************** +The 'n' items in a hash are distributed into 'k' buckets. Ideally each bucket +would contain an equal share '(n/k)' of the items. In other words, the maximum +linear position of any item in a bucket chain would be 'n/k' if every bucket is +equally used. If some buckets are overused and others are underused, the +overused buckets will contain items whose linear position surpasses 'n/k'. +Such items are considered non-ideal. + +As you might guess, `ideal%` is the percentage of ideal items in the hash. These +items have favorable linear positions in their bucket chains. As `ideal%` +approaches 100%, the hash table approaches constant-time lookup performance. +***************************************************************************** + +[[hashscan]] +hashscan +~~~~~~~~ +NOTE: This utility is only available on Linux, and on FreeBSD (8.1 and up). + +A utility called `hashscan` is included in the `tests/` directory. It +is built automatically when you run `make` in that directory. This tool +examines a running process and reports on the uthash tables that it finds in +that program's memory. It can also save the keys from each table in a format +that can be fed into `keystats`. + +Here is an example of using `hashscan`. First ensure that it is built: + + cd tests/ + make + +Since `hashscan` needs a running program to inspect, we'll start up a simple +program that makes a hash table and then sleeps as our test subject: + + ./test_sleep & + pid: 9711 + +Now that we have a test program, let's run `hashscan` on it: + + ./hashscan 9711 + Address ideal items buckets mc fl bloom/sat fcn keys saved to + ------------------ ----- -------- -------- -- -- --------- --- ------------- + 0x862e038 81% 10000 4096 11 ok 16 14% JEN + +If we wanted to copy out all its keys for external analysis using `keystats`, +add the `-k` flag: + + ./hashscan -k 9711 + Address ideal items buckets mc fl bloom/sat fcn keys saved to + ------------------ ----- -------- -------- -- -- --------- --- ------------- + 0x862e038 81% 10000 4096 11 ok 16 14% JEN /tmp/9711-0.key + +Now we could run `./keystats /tmp/9711-0.key` to analyze which hash function +has the best characteristics on this set of keys. + +hashscan column reference +^^^^^^^^^^^^^^^^^^^^^^^^^ +Address:: + virtual address of the hash table +ideal:: + The percentage of items in the table which can be looked up within an ideal + number of steps. See <> in the `keystats` section. +items:: + number of items in the hash table +buckets:: + number of buckets in the hash table +mc:: + the maximum chain length found in the hash table (uthash usually tries to + keep fewer than 10 items in each bucket, or in some cases a multiple of 10) +fl:: + flags (either `ok`, or `NX` if the expansion-inhibited flag is set) +bloom/sat:: + if the hash table uses a Bloom filter, this is the size (as a power of two) + of the filter (e.g. 16 means the filter is 2^16 bits in size). The second + number is the "saturation" of the bits expressed as a percentage. The lower + the percentage, the more potential benefit to identify cache misses quickly. +fcn:: + symbolic name of hash function +keys saved to:: + file to which keys were saved, if any + +.How hashscan works +***************************************************************************** +When hashscan runs, it attaches itself to the target process, which suspends +the target process momentarily. During this brief suspension, it scans the +target's virtual memory for the signature of a uthash hash table. It then +checks if a valid hash table structure accompanies the signature and reports +what it finds. When it detaches, the target process resumes running normally. +The hashscan is performed "read-only"-- the target process is not modified. +Since hashscan is analyzing a momentary snapshot of a running process, it may +return different results from one run to another. +***************************************************************************** + +[[expansion]] +Expansion internals +~~~~~~~~~~~~~~~~~~~ +Internally this hash manages the number of buckets, with the goal of having +enough buckets so that each one contains only a small number of items. + +.Why does the number of buckets matter? +******************************************************************************** +When looking up an item by its key, this hash scans linearly through the items +in the appropriate bucket. In order for the linear scan to run in constant +time, the number of items in each bucket must be bounded. This is accomplished +by increasing the number of buckets as needed. +******************************************************************************** + +Normal expansion +^^^^^^^^^^^^^^^^ +This hash attempts to keep fewer than 10 items in each bucket. When an item is +added that would cause a bucket to exceed this number, the number of buckets in +the hash is doubled and the items are redistributed into the new buckets. In an +ideal world, each bucket will then contain half as many items as it did before. + +Bucket expansion occurs automatically and invisibly as needed. There is +no need for the application to know when it occurs. + +Per-bucket expansion threshold +++++++++++++++++++++++++++++++ +Normally all buckets share the same threshold (10 items) at which point bucket +expansion is triggered. During the process of bucket expansion, uthash can +adjust this expansion-trigger threshold on a per-bucket basis if it sees that +certain buckets are over-utilized. + +When this threshold is adjusted, it goes from 10 to a multiple of 10 (for that +particular bucket). The multiple is based on how many times greater the actual +chain length is than the ideal length. It is a practical measure to reduce +excess bucket expansion in the case where a hash function over-utilizes a few +buckets but has good overall distribution. However, if the overall distribution +gets too bad, uthash changes tactics. + +Inhibited expansion +^^^^^^^^^^^^^^^^^^^ +You usually don't need to know or worry about this, particularly if you used +the `keystats` utility during development to select a good hash for your keys. + +A hash function may yield an uneven distribution of items across the buckets. +In moderation this is not a problem. Normal bucket expansion takes place as +the chain lengths grow. But when significant imbalance occurs (because the hash +function is not well suited to the key domain), bucket expansion may be +ineffective at reducing the chain lengths. + +Imagine a very bad hash function which always puts every item in bucket 0. No +matter how many times the number of buckets is doubled, the chain length of +bucket 0 stays the same. In a situation like this, the best behavior is to +stop expanding, and accept O(n) lookup performance. This is what uthash +does. It degrades gracefully if the hash function is ill-suited to the keys. + +If two consecutive bucket expansions yield `ideal%` values below 50%, uthash +inhibits expansion for that hash table. Once set, the 'bucket expansion +inhibited' flag remains in effect as long as the hash has items in it. +Inhibited expansion may cause `HASH_FIND` to exhibit worse than constant-time +performance. + +Hooks +~~~~~ +You don't need to use these hooks- they are only here if you want to modify +the behavior of uthash. Hooks can be used to change how uthash allocates +memory, and to run code in response to certain internal events. + +malloc/free +^^^^^^^^^^^ +By default this hash implementation uses `malloc` and `free` to manage memory. +If your application uses its own custom allocator, this hash can use them too. + +.Specifying alternate memory management functions +---------------------------------------------------------------------------- +#include "uthash.h" + +/* undefine the defaults */ +#undef uthash_malloc +#undef uthash_free + +/* re-define, specifying alternate functions */ +#define uthash_malloc(sz) my_malloc(sz) +#define uthash_free(ptr,sz) my_free(ptr) + +... +---------------------------------------------------------------------------- + +Notice that `uthash_free` receives two parameters. The `sz` parameter is for +convenience on embedded platforms that manage their own memory. + +Out of memory +^^^^^^^^^^^^^ +If memory allocation fails (i.e., the malloc function returned `NULL`), the +default behavior is to terminate the process by calling `exit(-1)`. This can +be modified by re-defining the `uthash_fatal` macro. + + #undef uthash_fatal + #define uthash_fatal(msg) my_fatal_function(msg); + +The fatal function should terminate the process or `longjmp` back to a safe +place. Uthash does not support "returning a failure" if memory cannot be +allocated. + +Internal events +^^^^^^^^^^^^^^^ +There is no need for the application to set these hooks or take action in +response to these events. They are mainly for diagnostic purposes. + +These two hooks are "notification" hooks which get executed if uthash is +expanding buckets, or setting the 'bucket expansion inhibited' flag. Normally +both of these hooks are undefined and thus compile away to nothing. + +Expansion ++++++++++ +There is a hook for the bucket expansion event. + +.Bucket expansion hook +---------------------------------------------------------------------------- +#include "uthash.h" + +#undef uthash_expand_fyi +#define uthash_expand_fyi(tbl) printf("expanded to %d buckets\n", tbl->num_buckets) + +... +---------------------------------------------------------------------------- + +Expansion-inhibition +++++++++++++++++++++ +This hook can be defined to code to execute in the event that uthash decides to +set the 'bucket expansion inhibited' flag. + +.Bucket expansion inhibited hook +---------------------------------------------------------------------------- +#include "uthash.h" + +#undef uthash_noexpand_fyi +#define uthash_noexpand_fyi printf("warning: bucket expansion inhibited\n"); + +... +---------------------------------------------------------------------------- + + +Debug mode +~~~~~~~~~~ +If a program that uses this hash is compiled with `-DHASH_DEBUG=1`, a special +internal consistency-checking mode is activated. In this mode, the integrity +of the whole hash is checked following every add or delete operation. This is +for debugging the uthash software only, not for use in production code. + +In the `tests/` directory, running `make debug` will run all the tests in +this mode. + +In this mode, any internal errors in the hash data structure will cause a +message to be printed to `stderr` and the program to exit. + +The `UT_hash_handle` data structure includes `next`, `prev`, `hh_next` and +`hh_prev` fields. The former two fields determine the "application" ordering +(that is, insertion order-- the order the items were added). The latter two +fields determine the "bucket chain" order. These link the `UT_hash_handles` +together in a doubly-linked list that is a bucket chain. + +Checks performed in `-DHASH_DEBUG=1` mode: + +- the hash is walked in its entirety twice: once in 'bucket' order and a + second time in 'application' order +- the total number of items encountered in both walks is checked against the + stored number +- during the walk in 'bucket' order, each item's `hh_prev` pointer is compared + for equality with the last visited item +- during the walk in 'application' order, each item's `prev` pointer is compared + for equality with the last visited item + +.Macro debugging: +******************************************************************************** +Sometimes it's difficult to interpret a compiler warning on a line which +contains a macro call. In the case of uthash, one macro can expand to dozens of +lines. In this case, it is helpful to expand the macros and then recompile. +By doing so, the warning message will refer to the exact line within the macro. + +Here is an example of how to expand the macros and then recompile. This uses the +`test1.c` program in the `tests/` subdirectory. + + gcc -E -I../src test1.c > /tmp/a.c + egrep -v '^#' /tmp/a.c > /tmp/b.c + indent /tmp/b.c + gcc -o /tmp/b /tmp/b.c + +The last line compiles the original program (test1.c) with all macros expanded. +If there was a warning, the referenced line number can be checked in `/tmp/b.c`. +******************************************************************************** + +Thread safety +~~~~~~~~~~~~~ +You can use uthash in a threaded program. But you must do the locking. Use a +read-write lock to protect against concurrent writes. It is ok to have +concurrent readers (since uthash 1.5). + +For example using pthreads you can create an rwlock like this: + + pthread_rwlock_t lock; + if (pthread_rwlock_init(&lock,NULL) != 0) fatal("can't create rwlock"); + +Then, readers must acquire the read lock before doing any `HASH_FIND` calls or +before iterating over the hash elements: + + if (pthread_rwlock_rdlock(&lock) != 0) fatal("can't get rdlock"); + HASH_FIND_INT(elts, &i, e); + pthread_rwlock_unlock(&lock); + +Writers must acquire the exclusive write lock before doing any update. Add, +delete, and sort are all updates that must be locked. + + if (pthread_rwlock_wrlock(&lock) != 0) fatal("can't get wrlock"); + HASH_DEL(elts, e); + pthread_rwlock_unlock(&lock); + +If you prefer, you can use a mutex instead of a read-write lock, but this will +reduce reader concurrency to a single thread at a time. + +An example program using uthash with a read-write lock is included in +`tests/threads/test1.c`. + +[[Macro_reference]] +Macro reference +--------------- + +Convenience macros +~~~~~~~~~~~~~~~~~~ +The convenience macros do the same thing as the generalized macros, but +require fewer arguments. + +In order to use the convenience macros, + +1. the structure's `UT_hash_handle` field must be named `hh`, and +2. for add or find, the key field must be of type `int` or `char[]` or pointer + +.Convenience macros +[width="90%",cols="10m,30m",grid="none",options="header"] +|=============================================================================== +|macro | arguments +|HASH_ADD_INT | (head, keyfield_name, item_ptr) +|HASH_FIND_INT | (head, key_ptr, item_ptr) +|HASH_ADD_STR | (head, keyfield_name, item_ptr) +|HASH_FIND_STR | (head, key_ptr, item_ptr) +|HASH_ADD_PTR | (head, keyfield_name, item_ptr) +|HASH_FIND_PTR | (head, key_ptr, item_ptr) +|HASH_DEL | (head, item_ptr) +|HASH_SORT | (head, cmp) +|HASH_COUNT | (head) +|=============================================================================== + +General macros +~~~~~~~~~~~~~~ + +These macros add, find, delete and sort the items in a hash. You need to +use the general macros if your `UT_hash_handle` is named something other +than `hh`, or if your key's data type isn't `int` or `char[]`. + +.General macros +[width="90%",cols="10m,30m",grid="none",options="header"] +|=============================================================================== +|macro | arguments +|HASH_ADD | (hh_name, head, keyfield_name, key_len, item_ptr) +|HASH_ADD_KEYPTR| (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_FIND | (hh_name, head, key_ptr, key_len, item_ptr) +|HASH_DELETE | (hh_name, head, item_ptr) +|HASH_SRT | (hh_name, head, cmp) +|HASH_CNT | (hh_name, head) +|HASH_CLEAR | (hh_name, head) +|HASH_SELECT | (dst_hh_name, dst_head, src_hh_name, src_head, condition) +|HASH_ITER | (hh_name, head, item_ptr, tmp_item_ptr) +|=============================================================================== + +[NOTE] +`HASH_ADD_KEYPTR` is used when the structure contains a pointer to the +key, rather than the key itself. + + +Argument descriptions +^^^^^^^^^^^^^^^^^^^^^ +hh_name:: + name of the `UT_hash_handle` field in the structure. Conventionally called + `hh`. +head:: + the structure pointer variable which acts as the "head" of the hash. So + named because it initially points to the first item that is added to the hash. +keyfield_name:: + the name of the key field in the structure. (In the case of a multi-field + key, this is the first field of the key). If you're new to macros, it + might seem strange to pass the name of a field as a parameter. See + <>. +key_len:: + the length of the key field in bytes. E.g. for an integer key, this is + `sizeof(int)`, while for a string key it's `strlen(key)`. (For a + multi-field key, see the notes in this guide on calculating key length). +key_ptr:: + for `HASH_FIND`, this is a pointer to the key to look up in the hash + (since it's a pointer, you can't directly pass a literal value here). For + `HASH_ADD_KEYPTR`, this is the address of the key of the item being added. +item_ptr:: + pointer to the structure being added, deleted, or looked up, or the current + pointer during iteration. This is an input parameter for `HASH_ADD` and + `HASH_DELETE` macros, and an output parameter for `HASH_FIND` and + `HASH_ITER`. (When using `HASH_ITER` to iterate, `tmp_item_ptr` + is another variable of the same type as `item_ptr`, used internally). +cmp:: + pointer to comparison function which accepts two arguments (pointers to + items to compare) and returns an int specifying whether the first item + should sort before, equal to, or after the second item (like `strcmp`). +condition:: + a function or macro which accepts a single argument-- a void pointer to a + structure, which needs to be cast to the appropriate structure type. The + function or macro should return (or evaluate to) a non-zero value if the + structure should be "selected" for addition to the destination hash. + +// vim: set tw=80 wm=2 syntax=asciidoc: + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utarray.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utarray.txt new file mode 100644 index 0000000000..37830f1244 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utarray.txt @@ -0,0 +1,376 @@ +utarray: dynamic array macros for C +=================================== +Troy D. Hanson +v1.9.5, November 2011 + +include::sflogo.txt[] +include::topnav_utarray.txt[] + +Introduction +------------ +include::toc.txt[] + +A set of general-purpose dynamic array macros for C structures are included with +uthash in `utarray.h`. To use these macros in your own C program, just +copy `utarray.h` into your source directory and use it in your programs. + + #include "utarray.h" + +The dynamic array supports basic operations such as push, pop, and erase on the +array elements. These array elements can be any simple datatype or structure. +The array <> are based loosely on the C++ STL vector methods. + +Internally the dynamic array contains a contiguous memory region into which +the elements are copied. This buffer is grown as needed using `realloc` to +accomodate all the data that is pushed into it. + +Download +~~~~~~~~ +To download the `utarray.h` header file, follow the link on the +http://uthash.sourceforge.net[uthash home page]. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utarray' macros have been tested on: + + * Linux, + * Mac OS X, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Declaration +~~~~~~~~~~~ + +The array itself has the data type `UT_array`, regardless of the type of +elements to be stored in it. It is declared like, + + UT_array *nums; + +New and free +~~~~~~~~~~~~ +The next step is to create the array using `utarray_new`. Later when you're +done with the array, `utarray_free` will free it and all its elements. + +Push, pop, etc +~~~~~~~~~~~~~~ +The central features of the utarray involve putting elements into it, taking +them out, and iterating over them. There are several <> +to pick from that deal with either single elements or ranges of elements at a +time. In the examples below we will use only the push operation to insert +elements. + +Elements +-------- + +Support for dynamic arrays of integers or strings is especially easy. These are +best shown by example: + +Integers +~~~~~~~~ +This example makes a utarray of integers, pushes 0-9 into it, then prints it. +Lastly it frees it. + +.Integer elements +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +int main() { + UT_array *nums; + int i, *p; + + utarray_new(nums,&ut_int_icd); + for(i=0; i < 10; i++) utarray_push_back(nums,&i); + + for(p=(int*)utarray_front(nums); + p!=NULL; + p=(int*)utarray_next(nums,p)) { + printf("%d\n",*p); + } + + utarray_free(nums); + + return 0; +} +------------------------------------------------------------------------------- + +The second argument to `utarray_push_back` is always a 'pointer' to the type +(so a literal cannot be used). So for integers, it is an `int*`. + +Strings +~~~~~~~ +In this example we make a utarray of strings, push two strings into it, print +it and free it. + +.String elements +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +int main() { + UT_array *strs; + char *s, **p; + + utarray_new(strs,&ut_str_icd); + + s = "hello"; utarray_push_back(strs, &s); + s = "world"; utarray_push_back(strs, &s); + p = NULL; + while ( (p=(char**)utarray_next(strs,p))) { + printf("%s\n",*p); + } + + utarray_free(strs); + + return 0; +} +------------------------------------------------------------------------------- + +In this example, since the element is a `char*`, we pass a pointer to it +(`char**`) as the second argument to `utarray_push_back`. Note that "push" makes +a copy of the source string and pushes that copy into the array. + +About UT_icd +~~~~~~~~~~~~ + +Arrays be made of any type of element, not just integers and strings. The +elements can be basic types or structures. Unless you're dealing with integers +and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need +to define a `UT_icd` helper structure. This structure contains everything that +utarray needs to initialize, copy or destruct elements. + + typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; + } UT_icd; + +The three function pointers `init`, `copy`, and `dtor` have these prototypes: + + typedef void (ctor_f)(void *dst, const void *src); + typedef void (dtor_f)(void *elt); + typedef void (init_f)(void *elt); + +The `sz` is just the size of the element being stored in the array. + +The `init` function will be invoked whenever utarray needs to initialize an +empty element. This only happens as a byproduct of `utarray_resize` or +`utarray_extend_back`. If `init` is `NULL`, it defaults to zero filling the +new element using memset. + +The `copy` function is used whenever an element is copied into the array. +It is invoked during `utarray_push_back`, `utarray_insert`, `utarray_inserta`, +or `utarray_concat`. If `copy` is `NULL`, it defaults to a bitwise copy using +memcpy. + +The `dtor` function is used to clean up an element that is being removed from +the array. It may be invoked due to `utarray_resize`, `utarray_pop_back`, +`utarray_erase`, `utarray_clear`, `utarray_done` or `utarray_free`. If the +elements need no cleanup upon destruction, `dtor` may be `NULL`. + +Scalar types +~~~~~~~~~~~~ + +The next example uses `UT_icd` with all its defaults to make a utarray of +`long` elements. This example pushes two longs, prints them, and frees the +array. + +.long elements +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +UT_icd long_icd = {sizeof(long), NULL, NULL, NULL }; + +int main() { + UT_array *nums; + long l, *p; + utarray_new(nums, &long_icd); + + l=1; utarray_push_back(nums, &l); + l=2; utarray_push_back(nums, &l); + + p=NULL; + while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p); + + utarray_free(nums); + return 0; +} +------------------------------------------------------------------------------- + +Structures +~~~~~~~~~~ + +Structures can be used as utarray elements. If the structure requires no +special effort to initialize, copy or destruct, we can use `UT_icd` with all +its defaults. This example shows a structure that consists of two integers. Here +we push two values, print them and free the array. + +.Structure (simple) +------------------------------------------------------------------------------- +#include +#include "utarray.h" + +typedef struct { + int a; + int b; +} intpair_t; + +UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL}; + +int main() { + + UT_array *pairs; + intpair_t ip, *p; + utarray_new(pairs,&intpair_icd); + + ip.a=1; ip.b=2; utarray_push_back(pairs, &ip); + ip.a=10; ip.b=20; utarray_push_back(pairs, &ip); + + for(p=(intpair_t*)utarray_front(pairs); + p!=NULL; + p=(intpair_t*)utarray_next(pairs,p)) { + printf("%d %d\n", p->a, p->b); + } + + utarray_free(pairs); + return 0; +} +------------------------------------------------------------------------------- + +The real utility of `UT_icd` is apparent when the elements of the utarray are +structures that require special work to initialize, copy or destruct. + +For example, when a structure contains pointers to related memory areas that +need to be copied when the structure is copied (and freed when the structure is +freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`. + +Here we take an example of a structure that contains an integer and a string. +When this element is copied (such as when an element is pushed into the array), +we want to "deep copy" the `s` pointer (so the original element and the new +element point to their own copies of `s`). When an element is destructed, we +want to "deep free" its copy of `s`. Lastly, this example is written to work +even if `s` has the value `NULL`. + +.Structure (complex) +------------------------------------------------------------------------------- +#include +#include +#include "utarray.h" + +typedef struct { + int a; + char *s; +} intchar_t; + +void intchar_copy(void *_dst, const void *_src) { + intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src; + dst->a = src->a; + dst->s = src->s ? strdup(src->s) : NULL; +} + +void intchar_dtor(void *_elt) { + intchar_t *elt = (intchar_t*)_elt; + if (elt->s) free(elt->s); +} + +UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor}; + +int main() { + UT_array *intchars; + intchar_t ic, *p; + utarray_new(intchars, &intchar_icd); + + ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic); + ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic); + + p=NULL; + while( (p=(intchar_t*)utarray_next(intchars,p))) { + printf("%d %s\n", p->a, (p->s ? p->s : "null")); + } + + utarray_free(intchars); + return 0; +} + +------------------------------------------------------------------------------- + +[[operations]] +Reference +--------- +This table lists all the utarray operations. These are loosely based on the C++ +vector class. + +Operations +~~~~~~~~~~ + +[width="100%",cols="50 +v1.9.5, November 2011 + +include::sflogo.txt[] +include::topnav_utlist.txt[] + +Introduction +------------ +include::toc.txt[] + +A set of general-purpose 'linked list' macros for C structures are included with +uthash in `utlist.h`. To use these macros in your own C program, just +copy `utlist.h` into your source directory and use it in your programs. + + #include "utlist.h" + +These macros support the basic linked list operations: adding and deleting +elements, sorting them and iterating over them. + +Download +~~~~~~~~ +To download the `utlist.h` header file, follow the link on the +http://uthash.sourceforge.net[uthash home page]. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utlist' macros have been tested on: + + * Linux, + * Mac OS X, and + * Windows, using Visual Studio 2008, Visual Studio 2010, or Cygwin/MinGW. + +Using utlist +------------ + +Types of lists +~~~~~~~~~~~~~~ +Three types of linked lists are supported: + +- *singly-linked* lists, +- *doubly-linked* lists, and +- *circular, doubly-linked* lists + +Efficiency +^^^^^^^^^^ +For all types of lists, prepending elements and deleting elements are +constant-time operations. Appending to a singly-linked list is an 'O(n)' +operation but appending to a doubly-linked list is constant time using these +macros. (This is because, in the utlist implementation of the doubly-linked +list, the head element's `prev` member points back to the list tail, even when +the list is non-circular). Sorting is an 'O(n log(n))' operation. Iteration +and searching are `O(n)` for all list types. + +List elements +~~~~~~~~~~~~~ +You can use any structure with these macros, as long as the structure +contains a `next` pointer. If you want to make a doubly-linked list, +the element also needs to have a `prev` pointer. + + typedef struct element { + char *name; + struct element *prev; /* needed for a doubly-linked list only */ + struct element *next; /* needed for singly- or doubly-linked lists */ + } element; + +You can name your structure anything. In the example above it is called `element`. +Within a particular list, all elements must be of the same type. + +List head +~~~~~~~~~ +The list head is simply a pointer to your element structure. You can name it +anything. *It must be initialized to `NULL`*. + + element *head = NULL; + +List operations +~~~~~~~~~~~~~~~ +The lists support inserting or deleting elements, sorting the elements and +iterating over them. + +[width="100%",cols="10 +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(el *a, el *b) { + return strcmp(a->bname,b->bname); +} + +el *head = NULL; /* important- initialize to NULL! */ + +int main(int argc, char *argv[]) { + el *name, *elt, *tmp, etmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + DL_APPEND(head, name); + } + DL_SORT(head, namecmp); + DL_FOREACH(head,elt) printf("%s", elt->bname); + + memcpy(&etmp.bname, "WES\n", 5); + DL_SEARCH(head,elt,&etmp,namecmp); + if (elt) printf("found %s\n", elt->bname); + + /* now delete each element, use the safe iterator */ + DL_FOREACH_SAFE(head,elt,tmp) { + DL_DELETE(head,elt); + } + + fclose(file); + + return 0; +} +-------------------------------------------------------------------------------- + +// vim: set nowrap syntax=asciidoc: + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utstring.txt b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utstring.txt new file mode 100644 index 0000000000..abfdcd1074 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/doc/txt/utstring.txt @@ -0,0 +1,178 @@ +utstring: dynamic string macros for C +===================================== +Troy D. Hanson +v1.9.5, November 2011 + +include::sflogo.txt[] +include::topnav_utstring.txt[] + +Introduction +------------ +include::toc.txt[] + +A set of very basic dynamic string macros for C programs are included with +uthash in `utstring.h`. To use these macros in your own C program, just +copy `utstring.h` into your source directory and use it in your programs. + + #include "utstring.h" + +The dynamic string supports basic operations such as inserting data (including +binary data-- despite its name, utstring is not limited to string content), +concatenation, getting the length and content, and clearing it. The string +<> are listed below. + +Download +~~~~~~~~ +To download the `utstring.h` header file, follow the link on the +http://uthash.sourceforge.net[uthash home page]. + +BSD licensed +~~~~~~~~~~~~ +This software is made available under the +link:license.html[revised BSD license]. +It is free and open source. + +Platforms +~~~~~~~~~ +The 'utstring' macros have been tested on: + + * Linux, + * Windows, using Visual Studio 2008 and Visual Studio 2010 + +Usage +----- + +Declaration +~~~~~~~~~~~ + +The dynamic string itself has the data type `UT_string`. It is declared like, + + UT_string *str; + +New and free +~~~~~~~~~~~~ +The next step is to create the string using `utstring_new`. Later when you're +done with it, `utstring_free` will free it and all its content. + +Manipulation +~~~~~~~~~~~~ +The `utstring_printf` or `utstring_bincpy` operations insert (copy) data into +the string. To concatenate one utstring to another, use `utstring_concat`. To +clear the content of the string, use `utstring_clear`. The length of the string +is available from `utstring_len`, and its content from `utstring_body`. This +evaluates to a `char*`. The buffer it points to is always null-terminated. +So, it can be used directly with external functions that expect a string. +This automatic null terminator is not counted in the length of the string. + +Samples +~~~~~~~ + +These examples show how to use utstring. + +.Sample 1 +------------------------------------------------------------------------------- +#include +#include "utstring.h" + +int main() { + UT_string *s; + + utstring_new(s); + utstring_printf(s, "hello world!" ); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} +------------------------------------------------------------------------------- + +The next example is meant to demonstrate that printf 'appends' to the string. +It also shows concatenation. + +.Sample 2 +------------------------------------------------------------------------------- +#include +#include "utstring.h" + +int main() { + UT_string *s, *t; + + utstring_new(s); + utstring_new(t); + + utstring_printf(s, "hello " ); + utstring_printf(s, "world " ); + + utstring_printf(t, "hi " ); + utstring_printf(t, "there " ); + + utstring_concat(s, t); + printf("length: %u\n", utstring_len(s)); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + utstring_free(t); + return 0; +} +------------------------------------------------------------------------------- + +The last example shows how binary data can be inserted into the string. It also +clears the string and prints new data into it. + +.Sample 3 +------------------------------------------------------------------------------- +#include +#include "utstring.h" + +int main() { + UT_string *s; + char binary[] = "\xff\xff"; + + utstring_new(s); + utstring_bincpy(s, binary, sizeof(binary)); + printf("length is %u\n", utstring_len(s)); + + utstring_clear(s); + utstring_printf(s,"number %d", 10); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} +------------------------------------------------------------------------------- + +[[operations]] +Reference +--------- +These are the utstring operations. + +Operations +~~~~~~~~~~ + +[width="100%",cols="50 /* size_t */ +#include /* memset, etc */ +#include /* exit */ + +#define oom() exit(-1) + +typedef void (ctor_f)(void *dst, const void *src); +typedef void (dtor_f)(void *elt); +typedef void (init_f)(void *elt); +typedef struct { + size_t sz; + init_f *init; + ctor_f *copy; + dtor_f *dtor; +} UT_icd; + +typedef struct { + unsigned i,n;/* i: index of next available slot, n: num slots */ + UT_icd icd; /* initializer, copy and destructor functions */ + char *d; /* n slots of size icd->sz*/ +} UT_array; + +#define utarray_init(a,_icd) do { \ + memset(a,0,sizeof(UT_array)); \ + (a)->icd=*_icd; \ +} while(0) + +#define utarray_done(a) do { \ + if ((a)->n) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + free((a)->d); \ + } \ + (a)->n=0; \ +} while(0) + +#define utarray_new(a,_icd) do { \ + a=(UT_array*)malloc(sizeof(UT_array)); \ + utarray_init(a,_icd); \ +} while(0) + +#define utarray_free(a) do { \ + utarray_done(a); \ + free(a); \ +} while(0) + +#define utarray_reserve(a,by) do { \ + if (((a)->i+by) > ((a)->n)) { \ + while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ + if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ + } \ +} while(0) + +#define utarray_push_back(a,p) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ + else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ +} while(0) + +#define utarray_pop_back(a) do { \ + if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ + else { (a)->i--; } \ +} while(0) + +#define utarray_extend_back(a) do { \ + utarray_reserve(a,1); \ + if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ + else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ + (a)->i++; \ +} while(0) + +#define utarray_len(a) ((a)->i) + +#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) +#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) + +#define utarray_insert(a,p,j) do { \ + utarray_reserve(a,1); \ + if (j > (a)->i) break; \ + if ((j) < (a)->i) { \ + memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ + else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ + (a)->i++; \ +} while(0) + +#define utarray_inserta(a,w,j) do { \ + if (utarray_len(w) == 0) break; \ + if (j > (a)->i) break; \ + utarray_reserve(a,utarray_len(w)); \ + if ((j) < (a)->i) { \ + memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ + _utarray_eltptr(a,j), \ + ((a)->i - (j))*((a)->icd.sz)); \ + } \ + if ((a)->icd.copy) { \ + size_t _ut_i; \ + for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ + (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ + } \ + } else { \ + memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ + utarray_len(w)*((a)->icd.sz)); \ + } \ + (a)->i += utarray_len(w); \ +} while(0) + +#define utarray_resize(dst,num) do { \ + size_t _ut_i; \ + if (dst->i > (size_t)(num)) { \ + if ((dst)->icd.dtor) { \ + for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ + (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ + } \ + } \ + } else if (dst->i < (size_t)(num)) { \ + utarray_reserve(dst,num-dst->i); \ + if ((dst)->icd.init) { \ + for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ + (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ + } \ + } else { \ + memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ + } \ + } \ + dst->i = num; \ +} while(0) + +#define utarray_concat(dst,src) do { \ + utarray_inserta((dst),(src),utarray_len(dst)); \ +} while(0) + +#define utarray_erase(a,pos,len) do { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < len; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ + } \ + } \ + if ((a)->i > (pos+len)) { \ + memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ + (((a)->i)-(pos+len))*((a)->icd.sz)); \ + } \ + (a)->i -= (len); \ +} while(0) + +#define utarray_renew(a,u) do { \ + if (a) utarray_clear(a); \ + else utarray_new((a),(u)); \ +} while(0) + +#define utarray_clear(a) do { \ + if ((a)->i > 0) { \ + if ((a)->icd.dtor) { \ + size_t _ut_i; \ + for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ + (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ + } \ + } \ + (a)->i = 0; \ + } \ +} while(0) + +#define utarray_sort(a,cmp) do { \ + qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ +} while(0) + +#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) + +#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) +#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) +#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) +#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) +#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1) + +/* last we pre-define a few icd for common utarrays of ints and strings */ +static void utarray_str_cpy(void *dst, const void *src) { + char **_src = (char**)src, **_dst = (char**)dst; + *_dst = (*_src == NULL) ? NULL : strdup(*_src); +} +static void utarray_str_dtor(void *elt) { + char **eltc = (char**)elt; + if (*eltc) free(*eltc); +} +static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; +static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; +static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; + + +#endif /* UTARRAY_H */ diff --git a/ami_test_client/libc-jss/uthash-1.9.6/src/uthash.h b/ami_test_client/libc-jss/uthash-1.9.6/src/uthash.h new file mode 100644 index 0000000000..9f83fc34f1 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/src/uthash.h @@ -0,0 +1,915 @@ +/* +Copyright (c) 2003-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#else +#include /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.6 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = (unsigned)keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + uint32_t _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + const uint8_t *_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + uint32_t _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/ami_test_client/libc-jss/uthash-1.9.6/src/utlist.h b/ami_test_client/libc-jss/uthash-1.9.6/src/utlist.h new file mode 100644 index 0000000000..1578acad2f --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/src/utlist.h @@ -0,0 +1,522 @@ +/* +Copyright (c) 2007-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.6 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +#define _PREV(elt,list) ((char*)((list)->prev)) +#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list) ((elt)->next) +#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) +#define _PREV(elt,list) ((elt)->prev) +#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define DL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define CDL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + LDECLTYPE(list) _tmp2; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp2,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_CONCAT(head1,head2) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = head1; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + head = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ +#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ +#endif +/* end VS2008 replacements */ + +#define LL_FOREACH(head,el) \ + for(el=head;el;el=(el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + LL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ +do { \ + LL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + _tmp = (head2)->prev; \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + (head1)->prev = _tmp; \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ +do { \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + + +#define DL_FOREACH(head,el) \ + for(el=head;el;el=(el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#endif /* UTLIST_H */ + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/src/utstring.h b/ami_test_client/libc-jss/uthash-1.9.6/src/utstring.h new file mode 100644 index 0000000000..a181ad7785 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/src/utstring.h @@ -0,0 +1,148 @@ +/* +Copyright (c) 2008-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +/* a dynamic string implementation using macros + * see http://uthash.sourceforge.net/utstring + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 1.9.6 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include +#include +#include +#define oom() exit(-1) + +typedef struct { + char *d; + size_t n; /* allocd size */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ + if ((s)->d == NULL) oom(); \ + (s)->n += amt; \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + s = (UT_string*)calloc(sizeof(UT_string),1); \ + if (!s) oom(); \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += l; \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((unsigned)((s)->i)) + +#define utstring_body(s) ((s)->d) + +_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + while (1) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && (n < (int)(s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +#endif /* UTSTRING_H */ diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/Makefile b/ami_test_client/libc-jss/uthash-1.9.6/tests/Makefile new file mode 100644 index 0000000000..3d25cb3732 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/Makefile @@ -0,0 +1,106 @@ +HASHDIR = ../src +FUNCS = BER SAX FNV OAT JEN SFH +SPECIAL_FUNCS = MUR +UTILS = emit_keys +PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9 \ + test10 test11 test12 test13 test14 test15 test16 test17 \ + test18 test19 test20 test21 test22 test23 test24 test25 \ + test26 test27 test28 test29 test30 test31 test32 test33 \ + test34 test35 test36 test37 test38 test39 test40 test41 \ + test42 test43 test44 test45 test46 test47 test48 test49 \ + test50 test51 test52 test53 test54 test55 test56 test57 \ + test58 test59 test60 test61 test62 test63 test64 test65 +CFLAGS = -I$(HASHDIR) +#CFLAGS += -DHASH_BLOOM=16 +#CFLAGS += -O2 +CFLAGS += -g +#CFLAGS += -Wstrict-aliasing=2 +CFLAGS += -Wall +#CFLAGS += -std=c89 +CFLAGS += ${EXTRA_CFLAGS} + +ifeq ($(HASH_DEBUG),1) +CFLAGS += -DHASH_DEBUG=1 +endif + +ifeq ($(HASH_PEDANTIC),1) +CFLAGS += -pedantic +endif + +TEST_TARGET=run_tests +TESTS=./do_tests + +MUR_CFLAGS = -DHASH_USING_NO_STRICT_ALIASING -fno-strict-aliasing +# On GNU we use -fno-strict-aliasing when using the Murmurhash +ifneq ($(strip $(shell $(CC) -v 2>&1 |egrep "gcc")),) +ifeq ($(HASH_FUNCTION),"HASH_MUR") + CFLAGS += $(MUR_CFLAGS) +endif +endif + +# detect Cygwin +ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "cygwin")),) + TESTS=./do_tests.cygwin +endif + +# detect MinGW +ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "mingw")),) + TEST_TARGET=run_tests_mingw + TESTS=./do_tests.mingw +endif + +#detect Linux (platform specific utilities) +ifneq ($(strip $(shell $(CC) -v 2>&1 |grep "linux")),) + PLAT_UTILS = hashscan sleep_test +endif + +#detect FreeBSD (platform specific utilities) +ifeq ($(strip $(shell uname -s)), FreeBSD) + ifeq ($(shell if [ `sysctl -n kern.osreldate` -ge 0801000 ]; then echo "ok"; fi), ok) + PLAT_UTILS = hashscan sleep_test + endif +endif + + +all: $(PROGS) $(UTILS) $(PLAT_UTILS) $(FUNCS) $(SPECIAL_FUNCS) $(TEST_TARGET) + +tests_only: $(PROGS) $(TEST_TARGET) + +debug: + $(MAKE) all HASH_DEBUG=1 + +pedantic: + $(MAKE) all HASH_PEDANTIC=1 + +cplusplus: + CC=$(CXX) $(MAKE) all + +example: example.c $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) -o $@ $(@).c + +$(PROGS) $(UTILS) : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) -o $@ $(@).c + +hashscan : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) $(MUR_CFLAGS) -o $@ $(@).c + +sleep_test : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) -DHASH_BLOOM=16 -o $@ $(@).c + +$(FUNCS) : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) -DHASH_FUNCTION=HASH_$@ -o keystat.$@ keystat.c + +$(SPECIAL_FUNCS) : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) $(MUR_CFLAGS) -DHASH_FUNCTION=HASH_$@ -o keystat.$@ keystat.c + +run_tests: $(PROGS) + perl $(TESTS) + +run_tests_mingw: $(PROGS) + /bin/sh do_tests.mingw + +.PHONY: clean + +clean: + rm -f $(UTILS) $(PLAT_UTILS) $(PROGS) test*.out keystat.??? example *.exe + rm -rf *.dSYM diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/README b/ami_test_client/libc-jss/uthash-1.9.6/tests/README new file mode 100644 index 0000000000..3984db0d62 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/README @@ -0,0 +1,98 @@ +Automated tests for uthash +============================================================================== +Run "make" in this directory to build the tests and run them. + +test1: make 10-item hash, iterate and print each one +test2: make 10-item hash, lookup items with even keys, print +test3: make 10-item hash, delete items with even keys, print others +test4: 10 structs have dual hash handles, separate keys +test5: 10 structs have dual hash handles, lookup evens by alt key +test6: test alt malloc macros +test7: test alt malloc macros with 1000 structs so bucket expansion occurs +test8: test num_items counter in UT_hash_handle +test9: test "find" after bucket expansion +test10: dual-hash handle test, bucket expansion on one and not the other +test11: read dat file of names into hash, sort them and print +test12: create hash with string keys, add 10 items, lookup each item +test13: make 10-item hash, delete items with even keys, reverse print others +test14: read dat file of names into hash, read file again and lookup each one +test15: build string-keyed hash of 3 items, lookup one item (c.f. test40.c) +test16: hash on aggregate key, iterate, lookup, using generalized macros +test17: sort, add more items, sort again +test18: test pathological HASH_DEL(a,a) scenario (single head,deletee variable) +test19: sort two hash tables with shared elements using HASH_SRT +test20: test a 5-byte "binary" key +test21: test a structure key (userguide) +test22: test multi-field key using flexible array member (userguide utf32) +test23: test whether delete in iteration works +test24: make 10-item hash and confirm item count (HASH_COUNT) +test25: CDL / DL / LL tests +test26: test the linked list sort macros in utlist.h +test27: LL_APPEND, SORT +test28: CDL / DL / LL tests +test29: DL_APPEND, SORT +test30: CDL_PREPEND, SORT +test31: CDL_PREPEND, SORT +test32: DL_PREPEND +test33: LL_PREPEND +test34: CDL_PREPEND +test35: CDL_PREPEND +test36: HASH_SELECT +test37: HASH_CLEAR +test38: find-or-add test on integer keys in short loop +test39: HASH_ADD_KEYPTR then HASH_FIND using array element as key pointer +test40: HASH_ADD_KEYPTR on string keys; pointer equivalent to test15.c +test41: test LL_FOREACH_SAFE,DL_FOREACH_SAFE,CDL_FOREACH_SAFE +test42: test LL_SEARCH, LL_SEARCH_SCALAR, and DL and CDL counterparts +test43: test utarray with intpair objects +test44: test utarray with int objects +test45: test utarray with int objects +test46: test utarray with char* objects +test47: test utstring +test48: test utarray of int +test49: test utarray of str +test50: test utarray of long +test51: test utarray of intpair +test52: test utarray of intchar +test53: test utstring +test54: test utstring +test55: test utstring +test56: test uthash, utlist and utstring together for #define conflicts etc +test57: test uthash HASH_ADD_PTR and HASH_FIND_PTR +test58: test HASH_ITER macro +test59: sample of multi-level hash +test60: sample of multi-level hash that also does HASH_DEL and free +test61: test utarray_find +test62: test macros used in safe unaligned reads on non-Intel type platforms +test63: LL_CONCAT test +test64: DL_CONCAT test +test65: LRU cache example courtesy of jehiah.cz with modifications +test66: test example where output variable to HASH_FIND needs extra parens +test67: test utarray_prev + +Other Make targets +================================================================================ +pedantic: makes the tests with extra CFLAGS for pedantic compiling +cplusplus: compiles all the C tests using the C++ compiler to test compatibility +debug: makes the tests with debugging symbols and no optimization +example: builds the 'example' program from the user guide +================================================================================ + +Other files +================================================================================ +keystats: key statistics analyzer. See the User Guide (http://uthash.sf.net) +emit_keys: reads a data file of unique strings, emits as keys w/HASH_EMIT_KEYS=1 +all_funcs: a script which executes the test suite with every hash function +win32tests:builds and runs the test suite under Microsoft Visual Studio + +LINUX-ONLY +---------- +hashscan: tool to examine a running process and get info on its hash tables +test_sleep:used as a subject for inspection by hashscan + +Manual performance testing +================================================================================ + # test performance characteristics on keys that are English dictionary words + emit_keys /usr/share/dict/words > words.keys + ./keystats words.keys + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/all_funcs b/ami_test_client/libc-jss/uthash-1.9.6/tests/all_funcs new file mode 100755 index 0000000000..18df8a1a27 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/all_funcs @@ -0,0 +1,14 @@ +#!/bin/sh + +function proceed { + read -p "proceed ? [n] " response + if [ "$response" != "y" ]; then exit -1; fi +} + +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_BER'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SAX'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_FNV'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_OAT'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_JEN'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_MUR'; proceed +make clean tests_only EXTRA_CFLAGS='-DHASH_FUNCTION=HASH_SFH'; diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.c new file mode 100644 index 0000000000..58b30024bb --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.c @@ -0,0 +1,64 @@ +#include /* malloc */ +#include /* gettimeofday */ +#include /* perror */ +#include /* printf */ +#include "uthash.h" + +#define BUFLEN 20 +#if 0 +#undef uthash_expand_fyi +#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets) +#endif + +typedef struct name_rec { + char boy_name[BUFLEN]; + UT_hash_handle hh; +} name_rec; + +int main(int argc,char *argv[]) { + name_rec *name, *names=NULL; + char linebuf[BUFLEN]; + FILE *file; + int i=0,j,nloops=3,loopnum=0,miss; + struct timeval tv1,tv2; + long elapsed_usec; + if (argc > 1) nloops = atoi(argv[1]); + + if ( (file = fopen( "test14.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + i++; + if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) exit(-1); + strncpy(name->boy_name,linebuf,BUFLEN); + HASH_ADD_STR(names,boy_name,name); + } + + again: + if (fseek(file,0,SEEK_SET) == -1) { + fprintf(stderr,"fseek failed: %s\n", strerror(errno)); + } + j=0; + + if (gettimeofday(&tv1,NULL) == -1) perror("gettimeofday: "); + while (fgets(linebuf,BUFLEN,file) != NULL) { + /* if we do 10 loops, the first has a 0% miss rate, + * the second has a 10% miss rate, etc */ + miss = ((rand()*1.0/RAND_MAX) < (loopnum*1.0/nloops)) ? 1 : 0; + /* generate a miss if we want one */ + if (miss) { linebuf[0]++; if (linebuf[1] != '\0') linebuf[1]++; } + HASH_FIND_STR(names,linebuf,name); + if (name) j++; + } + if (gettimeofday(&tv2,NULL) == -1) perror("gettimeofday: "); + elapsed_usec = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec); + printf("lookup on %d of %d (%.2f%%) names succeeded (%.2f usec)\n", j, i, + j*100.0/i, (double)(elapsed_usec)); + if (++loopnum < nloops) goto again; + fclose(file); + + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.sh b/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.sh new file mode 100755 index 0000000000..0a04f230a7 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/bloom_perf.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +BITS="16" + +cc -I../src -O3 -Wall -m64 bloom_perf.c -o bloom_perf.none +for bits in $BITS +do +cc -I../src -DHASH_BLOOM=$bits -O3 -Wall -m64 bloom_perf.c -o bloom_perf.$bits +done + +for bits in none $BITS +do +echo +echo "using $bits-bit filter:" +./bloom_perf.$bits 10 +done + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests new file mode 100755 index 0000000000..574403f2d4 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "test*[0-9]") { + push @tests, $_ if -e "$_.ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out`; + `diff $test.out $test.ans`; + print "$test failed\n" if $?; + $num_failed++ if $?; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; +exit $num_failed; diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.cygwin b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.cygwin new file mode 100755 index 0000000000..0c01ebee0b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.cygwin @@ -0,0 +1,22 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "test*[0-9].exe") { + push @tests, "$_" if -e substr($_, 0, - 4).".ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out`; + my $ansfile = substr($test, 0, - 4).".ans"; + `diff $test.out $ansfile`; + print "$test failed\n" if $?; + $num_failed++ if $?; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; +exit $num_failed; diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.mingw b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.mingw new file mode 100644 index 0000000000..1820334448 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests.mingw @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "MinGW test script starting" + +for f in test*.exe +do + t=`echo $f | sed s/.exe//` + `$f > $t.out` + diff -qb "$t.out" "$t.ans" + if [ $? -eq 1 ] + then + echo "$f failed" + else + true # can't have empty else + #echo "$f passed" + fi +done + +echo +echo "All tests complete" diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests_win32.cmd b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests_win32.cmd new file mode 100644 index 0000000000..be89301701 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/do_tests_win32.cmd @@ -0,0 +1,16 @@ +:: this compiles and runs the test suite under Visual Studio 2008 +::@echo off +call "C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" > vc.out +::call "C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat" > vc.out +set "COMPILE=cl.exe /I ..\src /EHsc /nologo" +echo compiling... +%COMPILE% tdiff.cpp > compile.out +::for %%f in (test*.c) do %COMPILE% /Tp %%f >> compile.out +for %%f in (test*.c) do %COMPILE% /Tc %%f >> compile.out +echo running tests... +for %%f in (test*.exe) do %%f > %%~nf.out +echo scanning for failures... +for %%f in (test*.out) do tdiff %%f %%~nf.ans +echo tests completed +::for %%f in (test*.out test*.obj test*.exe vc.out compile.out tdiff.obj tdiff.exe) do del %%f +pause diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/emit_keys.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/emit_keys.c new file mode 100644 index 0000000000..8a0c0cd786 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/emit_keys.c @@ -0,0 +1,44 @@ +#include /* malloc */ +#include /* perror */ +#include /* printf */ +#include /* write */ + +/* this define must precede uthash.h */ +#define HASH_EMIT_KEYS 1 +#include "uthash.h" + +#define BUFLEN 30 + +typedef struct name_rec { + char boy_name[BUFLEN]; + UT_hash_handle hh; +} name_rec; + +int main(int argc,char *argv[]) { + name_rec *name, *names=NULL; + char linebuf[BUFLEN]; + FILE *file; + int i=0; + + if (argc != 2) { + fprintf(stderr,"usage: %s file\n", argv[0]); + exit(-1); + } + + if ( (file = fopen( argv[1], "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) exit(-1); + strncpy(name->boy_name,linebuf,BUFLEN); + HASH_ADD_STR(names,boy_name,name); + i++; + } + + fprintf(stderr,"%d keys emitted.\n", i); + fclose(file); + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/example.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/example.c new file mode 100644 index 0000000000..0633629a74 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/example.c @@ -0,0 +1,125 @@ +#include /* gets */ +#include /* atoi, malloc */ +#include /* strcpy */ +#include "uthash.h" + +struct my_struct { + int id; /* key */ + char name[10]; + UT_hash_handle hh; /* makes this structure hashable */ +}; + +struct my_struct *users = NULL; + +void add_user(int user_id, char *name) { + struct my_struct *s; + + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + s->id = user_id; + strcpy(s->name, name); + HASH_ADD_INT( users, id, s ); /* id: name of key field */ +} + +struct my_struct *find_user(int user_id) { + struct my_struct *s; + + HASH_FIND_INT( users, &user_id, s ); /* s: output pointer */ + return s; +} + +void delete_user(struct my_struct *user) { + HASH_DEL( users, user); /* user: pointer to deletee */ + free(user); +} + +void delete_all() { + struct my_struct *current_user, *tmp; + + HASH_ITER(hh, users, current_user, tmp) { + HASH_DEL(users,current_user); /* delete it (users advances to next) */ + free(current_user); /* free it */ + } +} + +void print_users() { + struct my_struct *s; + + for(s=users; s != NULL; s=(struct my_struct*)(s->hh.next)) { + printf("user id %d: name %s\n", s->id, s->name); + } +} + +int name_sort(struct my_struct *a, struct my_struct *b) { + return strcmp(a->name,b->name); +} + +int id_sort(struct my_struct *a, struct my_struct *b) { + return (a->id - b->id); +} + +void sort_by_name() { + HASH_SORT(users, name_sort); +} + +void sort_by_id() { + HASH_SORT(users, id_sort); +} + +int main(int argc, char *argv[]) { + char in[10]; + int id=1, running=1; + struct my_struct *s; + unsigned num_users; + + while (running) { + printf("1. add user\n"); + printf("2. find user\n"); + printf("3. delete user\n"); + printf("4. delete all users\n"); + printf("5. sort items by name\n"); + printf("6. sort items by id\n"); + printf("7. print users\n"); + printf("8. count users\n"); + printf("9. quit\n"); + gets(in); + switch(atoi(in)) { + case 1: + printf("name?\n"); + add_user(id++, gets(in)); + break; + case 2: + printf("id?\n"); + s = find_user(atoi(gets(in))); + printf("user: %s\n", s ? s->name : "unknown"); + break; + case 3: + printf("id?\n"); + s = find_user(atoi(gets(in))); + if (s) delete_user(s); + else printf("id unknown\n"); + break; + case 4: + delete_all(); + break; + case 5: + sort_by_name(); + break; + case 6: + sort_by_id(); + break; + case 7: + print_users(); + break; + case 8: + num_users=HASH_COUNT(users); + printf("there are %u users\n", num_users); + break; + case 9: + running=0; + break; + } + } + + delete_all(); /* free any structures */ + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/hashscan.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/hashscan.c new file mode 100644 index 0000000000..9944b07576 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/hashscan.c @@ -0,0 +1,588 @@ +/* +Copyright (c) 2005-2010, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include /* MAXPATHLEN */ +#include /* VM_PROT_* flags */ +#endif + +/* need this defined so offsetof can give us bloom offsets in UT_hash_table */ +#define HASH_BLOOM 16 +#include "uthash.h" + +#ifdef __FreeBSD__ +typedef struct { + void *start; + void *end; +} vma_t; +#else +typedef struct { + off_t start; + off_t end; + char perms[4]; /* rwxp */ + char device[5]; /* fd:01 or 00:00 */ +} vma_t; +#endif + +const uint32_t sig = HASH_SIGNATURE; +int verbose=0; +int getkeys=0; + +#define vv(...) do {if (verbose>0) printf(__VA_ARGS__);} while(0) +#define vvv(...) do {if (verbose>1) printf(__VA_ARGS__);} while(0) + +/* these id's are arbitrary, only meaningful within this file */ +#define JEN 1 +#define BER 2 +#define SFH 3 +#define SAX 4 +#define FNV 5 +#define OAT 6 +#define MUR 7 +#define NUM_HASH_FUNCS 8 /* includes id 0, the non-function */ +char *hash_fcns[] = {"???","JEN","BER","SFH","SAX","FNV","OAT","MUR"}; + +/* given a peer key/len/hashv, reverse engineer its hash function */ +int infer_hash_function(char *key, size_t keylen, uint32_t hashv) { + uint32_t obkt, ohashv, num_bkts=0x01000000; /* anything ok */ + /* BER SAX FNV OAT JEN SFH */ + HASH_JEN(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return JEN; + HASH_BER(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return BER; + HASH_SFH(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return SFH; + HASH_SAX(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return SAX; + HASH_FNV(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return FNV; + HASH_OAT(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return OAT; + HASH_MUR(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return MUR; + return 0; +} + +/* read peer's memory from addr for len bytes, store into our dst */ +#ifdef __FreeBSD__ +int read_mem(void *dst, pid_t pid, void *start, size_t len) { + struct ptrace_io_desc io_desc; + int ret; + + io_desc.piod_op = PIOD_READ_D; + io_desc.piod_offs = start; + io_desc.piod_addr = dst; + io_desc.piod_len = len; + + ret = ptrace(PT_IO, pid, (void *) &io_desc, 0); + + if (ret) { + vv("read_mem: ptrace failed: %s\n", strerror(errno)); + return -1; + } else if (io_desc.piod_len != len) { + vv("read_mem: short read!\n"); + return -1; + } + + return 0; +} +#else +int read_mem(void *dst, int fd, off_t start, size_t len) { + int rc; + size_t bytes_read=0; + if (lseek(fd, start, SEEK_SET) == (off_t)-1) { + fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + return -1; + } + while ( len && ((rc=read(fd, (char*)dst+bytes_read, len)) > 0)) { + len -= rc; + bytes_read += rc; + } + if (rc==-1) vv("read_mem failed (%s)\n",strerror(errno)); + if ((len != 0 && rc >= 0)) vv("INTERNAL ERROR\n"); + return (rc == -1) ? -1 : 0; +} +#endif + +/* later compensate for possible presence of bloom filter */ +char *tbl_from_sig_addr(char *sig) { + return (sig - offsetof(UT_hash_table,signature)); +} + +#define HS_BIT_TEST(v,i) (v[i/8] & (1U << (i%8))) +void found(int fd, char* peer_sig, pid_t pid) { + UT_hash_table *tbl=NULL; + UT_hash_bucket *bkts=NULL; + UT_hash_handle hh; + size_t i, bloom_len, bloom_bitlen, bloom_on_bits=0,bloom_off_bits=0; + char *peer_tbl, *peer_bloom_sig, *peer_bloom_nbits, *peer_bloombv_ptr, + *peer_bloombv, *peer_bkts, *peer_key, *peer_hh, *key=NULL, + *hash_fcn=NULL, sat[10]; + unsigned char *bloombv=NULL; + static int fileno=0; + char keyfile[50]; + unsigned char bloom_nbits=0; + int keyfd=-1, mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, + hash_fcn_hits[NUM_HASH_FUNCS], hash_fcn_winner; + unsigned max_chain=0; + uint32_t bloomsig; + double bloom_sat=0; + snprintf(sat,sizeof(sat)," "); + for(i=0; i < NUM_HASH_FUNCS; i++) hash_fcn_hits[i]=0; + + if (getkeys) { + snprintf(keyfile, sizeof(keyfile), "/tmp/%u-%u.key", (unsigned)pid,fileno++); + if ( (keyfd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { + fprintf(stderr, "can't open %s: %s\n", keyfile, strerror(errno)); + exit(-1); + } + } + + vv("found signature at peer %p\n", peer_sig); + peer_tbl = tbl_from_sig_addr(peer_sig); + vvv("reading table at peer %p\n", peer_tbl); + + if ( (tbl = (UT_hash_table*)malloc(sizeof(UT_hash_table))) == NULL) { + fprintf(stderr, "out of memory\n"); + exit(-1); + } +#ifdef __FreeBSD__ + if (read_mem(tbl, pid, (void *)peer_tbl, sizeof(UT_hash_table)) != 0) { +#else + if (read_mem(tbl, fd, (off_t)peer_tbl, sizeof(UT_hash_table)) != 0) { +#endif + fprintf(stderr, "failed to read peer memory\n"); + goto done; + } + + /* got the table. how about the buckets */ + peer_bkts = (char*)tbl->buckets; + vvv("reading buckets at peer %p\n", peer_bkts); + bkts = (UT_hash_bucket*)malloc(sizeof(UT_hash_bucket)*tbl->num_buckets); + if (bkts == NULL) { + fprintf(stderr, "out of memory\n"); + exit(-1); + } +#ifdef __FreeBSD__ + if (read_mem(bkts, pid, (void *)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) { +#else + if (read_mem(bkts, fd, (off_t)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) { +#endif + fprintf(stderr, "failed to read peer memory\n"); + goto done; + } + + vvv("scanning %u peer buckets\n", tbl->num_buckets); + for(i=0; i < tbl->num_buckets; i++) { + vvv("bucket %u has %u items\n", (unsigned)i, (unsigned)(bkts[i].count)); + if (bkts[i].count > max_chain) max_chain = bkts[i].count; + if (bkts[i].expand_mult) vvv(" bucket %u has expand_mult %u\n", (unsigned)i, (unsigned)(bkts[i].expand_mult)); + + vvv("scanning bucket %u chain:\n", (unsigned)i); + peer_hh = (char*)bkts[i].hh_head; + while(peer_hh) { +#ifdef __FreeBSD__ + if (read_mem(&hh, pid, (void *)peer_hh, sizeof(hh)) != 0) { +#else + if (read_mem(&hh, fd, (off_t)peer_hh, sizeof(hh)) != 0) { +#endif + fprintf(stderr, "failed to read peer memory\n"); + goto done; + } + if ((char*)hh.tbl != peer_tbl) goto done; + peer_hh = (char*)hh.hh_next; + peer_key = (char*)(hh.key); + /* malloc space to read the key, and read it */ + if ( (key = (char*)malloc(sizeof(hh.keylen))) == NULL) { + fprintf(stderr, "out of memory\n"); + exit(-1); + } +#ifdef __FreeBSD__ + if (read_mem(key, pid, (void*)peer_key, hh.keylen) != 0) { +#else + if (read_mem(key, fd, (off_t)peer_key, hh.keylen) != 0) { +#endif + fprintf(stderr, "failed to read peer memory\n"); + goto done; + } + hash_fcn_hits[infer_hash_function(key,hh.keylen,hh.hashv)]++; + /* write the key if requested */ + if (getkeys) { + write(keyfd, &hh.keylen, sizeof(unsigned)); + write(keyfd, key, hh.keylen); + } + free(key); key=NULL; + } + } + + /* does it have a bloom filter? */ + peer_bloom_sig = peer_tbl + offsetof(UT_hash_table, bloom_sig); + peer_bloombv_ptr = peer_tbl + offsetof(UT_hash_table, bloom_bv); + peer_bloom_nbits = peer_tbl + offsetof(UT_hash_table, bloom_nbits); + vvv("looking for bloom signature at peer %p\n", peer_bloom_sig); +#ifdef __FreeBSD__ + if ((read_mem(&bloomsig, pid, (void *)peer_bloom_sig, sizeof(uint32_t)) == 0) && + (bloomsig == HASH_BLOOM_SIGNATURE)) { +#else + if ((read_mem(&bloomsig, fd, (off_t)peer_bloom_sig, sizeof(uint32_t)) == 0) && + (bloomsig == HASH_BLOOM_SIGNATURE)) { +#endif + vvv("bloom signature (%x) found\n",bloomsig); + /* bloom found. get at bv, nbits */ +#ifdef __FreeBSD__ + if (read_mem(&bloom_nbits, pid, (void *)peer_bloom_nbits, sizeof(char)) == 0) { +#else + if (read_mem(&bloom_nbits, fd, (off_t)peer_bloom_nbits, sizeof(char)) == 0) { +#endif + /* scan bloom filter, calculate saturation */ + bloom_bitlen = (1ULL << bloom_nbits); + bloom_len = (bloom_bitlen / 8) + ((bloom_bitlen % 8) ? 1 : 0); + vvv("bloom bitlen is %u, bloom_bytelen is %u\n", (unsigned)bloom_bitlen, (unsigned)bloom_len); + if ( (bloombv = (unsigned char*)malloc(bloom_len)) == NULL) { + fprintf(stderr, "out of memory\n"); + exit(-1); + } + /* read the address of the bitvector in the peer, then read the bv itself */ +#ifdef __FreeBSD__ + if ((read_mem(&peer_bloombv, pid, (void *)peer_bloombv_ptr, sizeof(void*)) == 0) && + (read_mem(bloombv, pid, (void *)peer_bloombv, bloom_len) == 0)) { +#else + if ((read_mem(&peer_bloombv, fd, (off_t)peer_bloombv_ptr, sizeof(void*)) == 0) && + (read_mem(bloombv, fd, (off_t)peer_bloombv, bloom_len) == 0)) { +#endif + /* calculate saturation */ + vvv("read peer bloom bitvector from %p (%u bytes)\n", peer_bloombv, (unsigned)bloom_len); + for(i=0; i < bloom_bitlen; i++) { + if (HS_BIT_TEST(bloombv,(unsigned)i)) { + /* vvv("bit %u set\n",(unsigned)i); */ + bloom_on_bits++; + } else bloom_off_bits++; + } + vvv("there were %u on_bits among %u total bits\n", (unsigned)bloom_on_bits, (unsigned)bloom_bitlen); + bloom_sat = bloom_on_bits * 100.0 / bloom_bitlen; + snprintf(sat,sizeof(sat),"%2u %5.0f%%", bloom_nbits, bloom_sat); + } + } + } + + /* choose apparent hash function */ + hash_fcn_winner=0; + for(i=0;i hash_fcn_hits[hash_fcn_winner]) hash_fcn_winner=i; + } + hash_fcn = hash_fcns[hash_fcn_winner]; + +/* +Address items ideal buckets mxch/<10 fl bloom/sat fcn keys saved to +------------------ -------- ----- -------- -------- -- --------- --- ------------- +0x0123456789abcdef 10000000 98% 32000000 10 100% ok BER /tmp/9110-0.key +0x0123456789abcdef 10000000 100% 32000000 9 90% NX 27/0.010% BER /tmp/9110-1.key +*/ + printf("Address ideal items buckets mc fl bloom/sat fcn keys saved to\n"); + printf("------------------ ----- -------- -------- -- -- --------- --- -------------\n"); + printf("%-18p %4.0f%% %8u %8u %2u %s %s %s %s\n", + (void*)peer_tbl, + (tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items, + tbl->num_items, + tbl->num_buckets, + max_chain, + tbl->noexpand ? "NX" : "ok", + sat, + hash_fcn, + (getkeys ? keyfile : "")); + +#if 0 + printf("read peer tbl:\n"); + printf("num_buckets: %u\n", tbl->num_buckets); + printf("num_items: %u\n", tbl->num_items); + printf("nonideal_items: %u (%.2f%%)\n", tbl->nonideal_items, + tbl->nonideal_items*100.0/tbl->num_items); + printf("expand: %s\n", tbl->noexpand ? "inhibited": "normal"); + if (getkeys) printf("keys written to %s\n", keyfile); +#endif + + done: + if (bkts) free(bkts); + if (tbl) free(tbl); + if (key) free(key); + if (keyfd != -1) close(keyfd); + if (bloombv) free(bloombv); +} + + +#ifdef __FreeBSD__ +void sigscan(pid_t pid, void *start, void *end, uint32_t sig) { + struct ptrace_io_desc io_desc; + int page_size = getpagesize(); + char *buf; + char *pos; + + /* make sure page_size is a multiple of the signature size, code below assumes this */ + assert(page_size % sizeof(sig) == 0); + + buf = malloc(page_size); + + if (buf == NULL) { + fprintf(stderr, "malloc failed in sigscan()\n"); + return; + } + + io_desc.piod_op = PIOD_READ_D; + io_desc.piod_offs = start; + io_desc.piod_addr = buf; + io_desc.piod_len = page_size; + + /* read in one page after another and search sig */ + while(!ptrace(PT_IO, pid, (void *) &io_desc, 0)) { + if (io_desc.piod_len != page_size) { + fprintf(stderr, "PT_IO returned less than page size in sigscan()\n"); + return; + } + + /* iterate over the the page using the signature size and look for the sig */ + for (pos = buf; pos < (buf + page_size); pos += sizeof(sig)) { + if (*(uint32_t *) pos == sig) { + found(pid, (char *) io_desc.piod_offs + (pos - buf), pid); + } + } + + /* + * 'end' is inclusive (the address of the last valid byte), so if the current offset + * plus a page is beyond 'end', we're already done. since all vm map entries consist + * of entire pages and 'end' is inclusive, current offset plus one page should point + * exactly one byte beyond 'end'. this is assert()ed below to be on the safe side. + */ + if (io_desc.piod_offs + page_size > end) { + assert(io_desc.piod_offs + page_size == (end + 1)); + break; + } + + /* advance to the next page */ + io_desc.piod_offs += page_size; + } +} +#else +void sigscan(int fd, off_t start, off_t end, uint32_t sig, pid_t pid) { + int rlen; + uint32_t u; + off_t at=0; + + if (lseek(fd, start, SEEK_SET) == (off_t)-1) { + fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + return; + } + + while ( (rlen = read(fd,&u,sizeof(u))) == sizeof(u)) { + if (!memcmp(&u,&sig,sizeof(u))) found(fd, (char*)(start+at),pid); + at += sizeof(u); + if ((off_t)(at + sizeof(u)) > end-start) break; + } + + if (rlen == -1) { + //fprintf(stderr,"read failed: %s\n", strerror(errno)); + //exit(-1); + } +} +#endif + + +#ifdef __FreeBSD__ +int scan(pid_t pid) { + vma_t *vmas=NULL, vma; + unsigned i, num_vmas = 0; + int ret; + struct ptrace_vm_entry vm_entry; + char path[MAXPATHLEN]; + + vv("attaching to peer\n"); + if (ptrace(PT_ATTACH,pid,NULL,0) == -1) { + fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno)); + exit(EXIT_FAILURE); + } + vv("waiting for peer to suspend temporarily\n"); + if (waitpid(pid,NULL,0) != pid) { + fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno)); + goto die; + } + + /* read memory map using ptrace */ + vv("listing peer virtual memory areas\n"); + vm_entry.pve_entry = 0; + vm_entry.pve_path = path; /* not used but required to make vm_entry.pve_pathlen work */ + while(1) { + /* set pve_pathlen every turn, it gets overwritten by ptrace */ + vm_entry.pve_pathlen = MAXPATHLEN; + errno = 0; + + ret = ptrace(PT_VM_ENTRY, pid, (void *) &vm_entry, 0); + + if (ret) { + if (errno == ENOENT) { + /* we've reached the last entry */ + break; + } + fprintf(stderr, "fetching vm map entry failed: %s (%i)\n", strerror(errno), errno); + goto die; + } + + vvv("vmmap entry: start: %p, end: %p", (void *) vm_entry.pve_start, (void *) vm_entry.pve_end); + + /* skip unreadable or vnode-backed entries */ + if (!(vm_entry.pve_prot & VM_PROT_READ) || vm_entry.pve_pathlen > 0) { + vvv(" -> skipped (not readable or vnode-backed)\n"); + vm_entry.pve_path[0] = 0; + continue; + } + + /* useful entry, add to list */ + vvv(" -> will be scanned\n"); + vma.start = (void *)vm_entry.pve_start; + vma.end = (void *)vm_entry.pve_end; + vmas = (vma_t *) realloc(vmas, (num_vmas + 1) * sizeof(vma_t)); + vmas[num_vmas++] = vma; + } + + vv("peer has %u virtual memory areas\n", num_vmas); + + /* look for the hash signature */ + vv("scanning peer memory for hash table signatures\n"); + for(i=0;i\n", prog); + exit(-1); +} + +int main(int argc, char *argv[]) { + pid_t pid; + int opt; + + while ( (opt = getopt(argc, argv, "kv")) != -1) { + switch (opt) { + case 'v': + verbose++; + break; + case 'k': + getkeys++; + break; + default: + usage(argv[0]); + break; + } + } + + if (optind < argc) pid=atoi(argv[optind++]); + else usage(argv[0]); + + return scan(pid); +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/keystat.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/keystat.c new file mode 100644 index 0000000000..bf1d6ff7f2 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/keystat.c @@ -0,0 +1,219 @@ +#include /* for 'open' */ +#include /* for 'open' */ +#include /* for 'open' */ +#include /* for 'malloc' */ +#include /* for 'printf' */ +#include /* for 'read' */ +#include /* for 'sterror' */ +#include /* for 'gettimeofday' */ +#include "uthash.h" + +#undef uthash_noexpand_fyi +#define uthash_noexpand_fyi(t) die() +#define UNALIGNED_KEYS 0 + +void die() { + fprintf(stderr,"expansion inhibited\n"); + exit(-1); +} + +/* Windows doesn't have gettimeofday. While Cygwin and some + * versions of MinGW supply one, it is very coarse. This substitute + * gives much more accurate elapsed times under Windows. */ +#if (( defined __CYGWIN__ ) || ( defined __MINGW32__ )) +#include +void win_gettimeofday(struct timeval* p, void* tz /* IGNORED */) { + LARGE_INTEGER q; + static long long freq; + static long long cyg_timer; + QueryPerformanceFrequency(&q); + freq = q.QuadPart; + QueryPerformanceCounter(&q); + cyg_timer = q.QuadPart; + p->tv_sec = (long)(cyg_timer / freq); + p->tv_usec = (long)(((cyg_timer % freq) * 1000000) / freq); +} +#define gettimeofday win_gettimeofday +#define MODE (O_RDONLY|O_BINARY) +#else +#define MODE (O_RDONLY) +#endif + +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +typedef struct stat_key { + char *key; + unsigned len; + UT_hash_handle hh, hh2; +} stat_key; + +#define CHAIN_0 0 +#define CHAIN_5 1 +#define CHAIN_10 2 +#define CHAIN_20 3 +#define CHAIN_100 4 +#define CHAIN_MAX 5 +void hash_chain_len_histogram(UT_hash_table *tbl) { + unsigned i, bkt_hist[CHAIN_MAX+1]; + double pct = 100.0/tbl->num_buckets; + memset(bkt_hist,0,sizeof(bkt_hist)); + for(i=0; i < tbl->num_buckets; i++) { + unsigned count = tbl->buckets[i].count; + if (count == 0) bkt_hist[CHAIN_0]++; + else if (count < 5) bkt_hist[CHAIN_5]++; + else if (count < 10) bkt_hist[CHAIN_10]++; + else if (count < 20) bkt_hist[CHAIN_20]++; + else if (count < 100) bkt_hist[CHAIN_100]++; + else bkt_hist[CHAIN_MAX]++; + } + fprintf(stderr, "Buckets with 0 items: %.1f%%\n", bkt_hist[CHAIN_0 ]*pct); + fprintf(stderr, "Buckets with < 5 items: %.1f%%\n", bkt_hist[CHAIN_5 ]*pct); + fprintf(stderr, "Buckets with < 10 items: %.1f%%\n", bkt_hist[CHAIN_10]*pct); + fprintf(stderr, "Buckets with < 20 items: %.1f%%\n", bkt_hist[CHAIN_20]*pct); + fprintf(stderr, "Buckets with < 100 items: %.1f%%\n", bkt_hist[CHAIN_100]*pct); + fprintf(stderr, "Buckets with > 100 items: %.1f%%\n", bkt_hist[CHAIN_MAX]*pct); +} + +int main(int argc, char *argv[]) { + int dups=0, rc, fd, done=0, err=0, want, i=0, padding=0, v=1, percent=100; + unsigned keylen, max_keylen=0, verbose=0; + const char *filename = "/dev/stdin"; + char *dst; + stat_key *keyt, *keytmp, *keys=NULL, *keys2=NULL; + struct timeval start_tm, end_tm, elapsed_tm, elapsed_tm2, elapsed_tm3; + + if ((argc >= 3) && !strcmp(argv[1],"-p")) {percent = atoi(argv[2]); v = 3;} + if ((argc >= v) && !strcmp(argv[v],"-v")) {verbose=1; v++;} + if (argc >= v) filename=argv[v]; + fd=open(filename,MODE); + + if ( fd == -1 ) { + fprintf(stderr,"open failed %s: %s\n", filename, strerror(errno)); + return -1; + } + + for(i=0; !done; i++) { + + want = sizeof(int); + dst = (char*)&keylen; + readmore1: + rc = read(fd,dst,want); + if (rc != want) { + if (rc == 0) done=1; + else if (rc == -1) { + fprintf(stderr,"read failed: %s\n", strerror(errno)); + err=1; + } + else if (rc > 0) { want -= rc; dst += rc; goto readmore1; } + } + + if (done || err) break; + if (keylen > max_keylen) max_keylen=keylen; + + if ( (keyt = (stat_key*)malloc(sizeof(stat_key))) == NULL) { + fprintf(stderr,"out of memory\n"); + exit(-1); + } + + /* read key */ +#ifdef UNALIGNED_KEYS + padding = i%8; +#endif + if ( (keyt->key = (char*)malloc(padding+keylen)) == NULL) { + fprintf(stderr,"out of memory\n"); + exit(-1); + } + keyt->key += padding; /* forcibly alter the alignment of key */ + keyt->len = keylen; + + want = keylen; + dst = keyt->key; + readmore2: + rc = read(fd,dst,want); + if (rc != want) { + if (rc == -1) { + fprintf(stderr,"read failed: %s\n", strerror(errno)); + err=1; + } else if (rc == 0) { + fprintf(stderr,"incomplete file\n"); + err=1; + } else if (rc >= 0) { want -= rc; dst += rc; goto readmore2; } + } + if (err) break; + /* if percent was set to something less than 100%, skip some keys*/ + if (((rand()*1.0) / RAND_MAX) > ((percent*1.0)/100)) { + free(keyt->key-padding); + free(keyt); + continue; + } + + /* eliminate dups */ + HASH_FIND(hh,keys,keyt->key,keylen,keytmp); + if (keytmp) { + dups++; + free(keyt->key - padding); + free(keyt); + } else { + HASH_ADD_KEYPTR(hh,keys,keyt->key,keylen,keyt); + } + } + + if (verbose) { + unsigned key_count = HASH_COUNT(keys); + fprintf(stderr,"max key length: %u\n", max_keylen); + fprintf(stderr,"number unique keys: %u\n", key_count); + fprintf(stderr,"keystats memory: %u\n", + (unsigned)((sizeof(stat_key)+max_keylen)*key_count)); + hash_chain_len_histogram(keys->hh.tbl); + } + + /* add all keys to a new hash, so we can measure add time w/o malloc */ + gettimeofday(&start_tm,NULL); + for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) { + HASH_ADD_KEYPTR(hh2,keys2,keyt->key,keyt->len,keyt); + } + gettimeofday(&end_tm,NULL); + timersub(&end_tm, &start_tm, &elapsed_tm); + + /* now look up all keys in the new hash, again measuring elapsed time */ + gettimeofday(&start_tm,NULL); + for(keyt = keys; keyt != NULL; keyt=(stat_key*)keyt->hh.next) { + HASH_FIND(hh2,keys2,keyt->key,keyt->len,keytmp); + if (!keytmp) fprintf(stderr,"internal error, key not found\n"); + } + gettimeofday(&end_tm,NULL); + timersub(&end_tm, &start_tm, &elapsed_tm2); + + /* now delete all items in the new hash, measuring elapsed time */ + gettimeofday(&start_tm,NULL); + while (keys2) { + keytmp = keys2; + HASH_DELETE(hh2,keys2,keytmp); + } + gettimeofday(&end_tm,NULL); + timersub(&end_tm, &start_tm, &elapsed_tm3); + + if (!err) { + printf("%.3f,%d,%d,%d,%s,%ld,%ld,%ld\n", + 1-(1.0*keys->hh.tbl->nonideal_items/keys->hh.tbl->num_items), + keys->hh.tbl->num_items, + keys->hh.tbl->num_buckets, + dups, + (keys->hh.tbl->noexpand ? "nx" : "ok"), + (elapsed_tm.tv_sec * 1000000) + elapsed_tm.tv_usec, + (elapsed_tm2.tv_sec * 1000000) + elapsed_tm2.tv_usec, + (elapsed_tm3.tv_sec * 1000000) + elapsed_tm3.tv_usec ); + } + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/keystats b/ami_test_client/libc-jss/uthash-1.9.6/tests/keystats new file mode 100755 index 0000000000..ea217b0d5b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/keystats @@ -0,0 +1,40 @@ +#!/usr/bin/perl + +use strict; + +use FindBin; + +sub usage { + print "usage: keystats [-v] keyfile\n"; + print "usage: keystats [-p [-v]] keyfile\n"; + exit -1; +} + +usage if ((@ARGV == 0) or ($ARGV[0] eq '-h')); + +my @exes = glob "$FindBin::Bin/keystat.???"; +my %stats; +for my $exe (@exes) { + $stats{$exe} = `$exe @ARGV`; + delete $stats{$exe} if ($? != 0); # omit hash functions that fail to produce stats (nx) +} + +print( "fcn ideal% #items #buckets dup% fl add_usec find_usec del-all usec\n"); +printf("--- ------ ---------- ---------- ----- -- ---------- ---------- ------------\n"); +for my $exe (sort statsort keys %stats) { + my ($ideal,$items,$bkts,$dups,$ok,$add,$find,$del) = split /,/, $stats{$exe}; + + # convert 0-1 values to percentages + $dups = $items ? (100.0 * $dups / $items) : 0.0; + $ideal = 100.0 * $ideal; + + printf("%3s %5.1f%% %10d %10d %4.0f%% %2s %10d %10d %12d\n", substr($exe,-3,3), + $ideal,$items,$bkts,$dups,$ok,$add,$find,$del); +} + +# sort on hash_q (desc) then by find_usec (asc) +sub statsort { + my @a_stats = split /,/, $stats{$a}; + my @b_stats = split /,/, $stats{$b}; + return ($b_stats[0] <=> $a_stats[0]) || ($a_stats[-1] <=> $b_stats[-1]); +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/mexpand b/ami_test_client/libc-jss/uthash-1.9.6/tests/mexpand new file mode 100755 index 0000000000..e1562d9c7c --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/mexpand @@ -0,0 +1,21 @@ +#!/bin/sh + +# utility to macro-expand a test program +CC=gcc +#CPPFLAGS=-DHASH_DEBUG=1 +#CC=g++ +CPPFLAGS="-m64 -O3" +CFLAGS="-O3 -m64 -pedantic -Wall" + +${CC} ${CPPFLAGS} -E -I../src $1 | egrep -v '^#' > /tmp/$1 +indent /tmp/$1 +${CC} ${CFLAGS} -o /tmp/$1.$$ /tmp/$1 +rm -f /tmp/$1.$$ + +read -p "open /tmp/$1 ? [n] " response +if [ "$response" = "y" ] +then + vi /tmp/$1 +fi + + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/simkeys.pl b/ami_test_client/libc-jss/uthash-1.9.6/tests/simkeys.pl new file mode 100755 index 0000000000..acc5583b73 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/simkeys.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl + +# This program generates a simkey10.dat (100, 1000, etc) each +# containing 100 random keys of length 10 (100, 1000, etc). +# These files can then be fed into keystats to observe that +# the time to add or find the keys is directly proportional to +# keylength n [in other words, O(n)]. +# +# The conclusion is that really long keys (e.g. 100k) are not +# efficient. TDH 23Jan07 + +use strict; +use warnings; + + +#for my $len (10,100,1000,10000,100000,1000000) { +for my $len (100) { + open OUTFILE, ">simkeys$len.dat" or die "can't open: $!\n"; + # we'll do 100 keys of $len + print "keylen $len\n"; + for my $i (0..99) { + my $key = pack "I", $len; + $key .= pack "C", (int(rand(256))) for (1..$len); + print OUTFILE $key; + } + close OUTFILE; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/sleep_test.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/sleep_test.c new file mode 100644 index 0000000000..ccc50c972f --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/sleep_test.c @@ -0,0 +1,29 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ +#include /* getpid */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL; + + /* create elements */ + for(i=0;i<10000;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + printf("pid: %u\n", (unsigned)getpid()); + /* printf("sig: %p\n", &users->hh.tbl->signature); */ + /* printf("bbv: %p\n", &users->hh.tbl->bloom_bv); */ + sleep(60*10); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/tdiff.cpp b/ami_test_client/libc-jss/uthash-1.9.6/tests/tdiff.cpp new file mode 100644 index 0000000000..4be14fed6e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/tdiff.cpp @@ -0,0 +1,34 @@ +// Windows does not have unix diff so this is a simple replacement +#include +#include +using namespace std; +int main(int argc, char *argv[] ) { + int rc=-1; + if (argc != 3) { + cout << "usage: " << argv[0] << " file1 file2\n"; + return -1; + } + char *file1 = argv[1]; + char *file2 = argv[2]; + ifstream is1(file1, ios::in); + ifstream is2(file2, ios::in); + if (is1.fail()) {cerr << "failed to open " << file1 << "\n"; goto done;} + if (is2.fail()) {cerr << "failed to open " << file2 << "\n"; goto done;} + char d1[256], d2[256]; + do { + is1.read(d1,sizeof(d1)); + is2.read(d2,sizeof(d2)); + if ((is1.gcount() != is2.gcount()) || memcmp(d1,d2,is1.gcount())) { + cout << file1 << " and " << file2 << " differ\n"; + goto done; + } + } while (!is1.eof() && !is2.eof()); + + rc=0; + + done: + is1.close(); + is2.close(); + return rc; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.ans new file mode 100644 index 0000000000..0b08c48c3d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.ans @@ -0,0 +1,10 @@ +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.c new file mode 100644 index 0000000000..afae6d6fa6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test1.c @@ -0,0 +1,27 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.ans new file mode 100644 index 0000000000..b1a27a8449 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.ans @@ -0,0 +1,4 @@ +9 found in hh +9 found in alth +10 not found in hh +10 found in alth diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.c new file mode 100644 index 0000000000..e8efc3bfcc --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test10.c @@ -0,0 +1,45 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; + UT_hash_handle alth; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL, *altusers=NULL; + + /* create elements */ + for(i=0;i<1000;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + if (i<10) HASH_ADD_INT(users,id,user); + HASH_ADD(alth,altusers,id,sizeof(int),user); + } + + /* + printf("hh items: %d, alth items: %d\n", + users->hh.tbl->num_items, users->alth.tbl->num_items); + printf("hh buckets: %d, alth buckets: %d\n", + users->hh.tbl->num_buckets, users->alth.tbl->num_buckets); + */ + + i=9; + HASH_FIND_INT(users,&i,tmp); + printf("%d %s in hh\n", i, (tmp ? "found" : "not found")); + HASH_FIND(alth,altusers,&i,sizeof(int),tmp); + printf("%d %s in alth\n", i, (tmp ? "found" : "not found")); + + i=10; + HASH_FIND_INT(users,&i,tmp); + printf("%d %s in hh\n", i, (tmp ? "found" : "not found")); + HASH_FIND(alth,altusers,&i,sizeof(int),tmp); + printf("%d %s in alth\n", i, (tmp ? "found" : "not found")); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.ans new file mode 100644 index 0000000000..2b72e985a9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.ans @@ -0,0 +1,51 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.c new file mode 100644 index 0000000000..81986c5b73 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.c @@ -0,0 +1,50 @@ +#include "uthash.h" +#include /* malloc */ +#include /* perror */ +#include /* printf */ + +#define BUFLEN 20 + +#if 0 +/* Print a message if the hash's no-expand flag is set. */ +#undef uthash_noexpand_fyi +#undef uthash_expand_fyi +#define uthash_noexpand_fyi(tbl) printf("noexpand set\n"); +#define uthash_expand_fyi(tbl) printf("hash expanded\n"); +#endif + +typedef struct name_rec { + char boy_name[BUFLEN]; + UT_hash_handle hh; +} name_rec; + +int namecmp(void *_a, void *_b) { + name_rec *a = (name_rec*)_a; + name_rec *b = (name_rec*)_b; + return strcmp(a->boy_name,b->boy_name); +} + +int main(int argc,char *argv[]) { + name_rec *name, *names=NULL; + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) exit(-1); + strncpy(name->boy_name,linebuf,BUFLEN); + HASH_ADD_STR(names,boy_name,name); + } + + fclose(file); + HASH_SORT(names,namecmp); + for(name=names;name;name=(name_rec*)(name->hh.next)) + printf("%s",name->boy_name); + + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.dat b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.dat new file mode 100644 index 0000000000..bb6051b750 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test11.dat @@ -0,0 +1,51 @@ +JOHN +WILLIAM +WALTER +DOUGLAS +GERALD +FREDERICK +WARREN +SHANE +LESTER +RON +HARVEY +ADRIAN +CODY +NELSON +CLIFTON +WILLARD +DOUG +ORLANDO +REX +OMAR +DAMON +LOWELL +IRVING +CARROLL +LAURENCE +ROLANDO +CARY +XAVIER +ISAIAH +GUS +JARVIS +WINFRED +RAYMUNDO +LINCOLN +CORNELL +NIGEL +NORMAND +FRITZ +DONN +TRINIDAD +ODIS +DANNIE +DARIO +KENTON +CHONG +NEVILLE +TONEY +WARNER +WES +COLTON +ARNOLDO diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.ans new file mode 100644 index 0000000000..727f397ebd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.ans @@ -0,0 +1,20 @@ +added bob (id 0) +added jack (id 1) +added gary (id 2) +added ty (id 3) +added bo (id 4) +added phil (id 5) +added art (id 6) +added gil (id 7) +added buck (id 8) +added ted (id 9) +found bob (id 0) +found jack (id 1) +found gary (id 2) +found ty (id 3) +found bo (id 4) +found phil (id 5) +found art (id 6) +found gil (id 7) +found buck (id 8) +found ted (id 9) diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.c new file mode 100644 index 0000000000..dfed3f4547 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test12.c @@ -0,0 +1,34 @@ +#include "uthash.h" +#include +#include /* malloc */ + +typedef struct person_t { + char first_name[10]; + int id; + UT_hash_handle hh; +} person_t; + +int main(int argc, char*argv[]) { + person_t *people=NULL, *person; + const char **name; + const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art", + "gil", "buck", "ted", NULL }; + int id=0; + + for(name=names; *name; name++) { + if ( (person = (person_t*)malloc(sizeof(person_t))) == NULL) exit(-1); + strncpy(person->first_name, *name,10); + person->id = id++; + HASH_ADD_STR(people,first_name,person); + printf("added %s (id %d)\n", person->first_name, person->id); + } + + for(name=names; *name; name++) { + HASH_FIND_STR(people,*name,person); + if (person) + printf("found %s (id %d)\n", person->first_name, person->id); + else + printf("failed to find %s\n", *name); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.ans new file mode 100644 index 0000000000..baf3ff215a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.ans @@ -0,0 +1,5 @@ +id 9, following prev... +id 7, following prev... +id 5, following prev... +id 3, following prev... +id 1, following prev... diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.c new file mode 100644 index 0000000000..ed624a865a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test13.c @@ -0,0 +1,42 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* delete each even ID */ + for(i=0;i<10;i+=2) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + HASH_DEL(users,tmp); + free(tmp); + } else printf("user id %d not found\n", i); + } + + i=9; + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + while(tmp) { + printf("id %d, following prev...\n", tmp->id); + tmp = (example_user_t*)tmp->hh.prev; + } + } else printf("user id %d not found\n", i); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.ans new file mode 100644 index 0000000000..77aeaebd01 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.ans @@ -0,0 +1 @@ +lookup on 1219 of 1219 names succeeded diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.c new file mode 100644 index 0000000000..c31f42a6af --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.c @@ -0,0 +1,46 @@ +#include "uthash.h" +#include /* malloc */ +#include /* perror */ +#include /* printf */ + +#define BUFLEN 20 +#if 0 +#undef uthash_expand_fyi +#define uthash_expand_fyi(tbl) printf("expanding to %d buckets\n", tbl->num_buckets) +#endif + +typedef struct name_rec { + char boy_name[BUFLEN]; + UT_hash_handle hh; +} name_rec; + +int main(int argc,char *argv[]) { + name_rec *name, *names=NULL; + char linebuf[BUFLEN]; + FILE *file; + int i=0,j=0; + + if ( (file = fopen( "test14.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + i++; + if ( (name = (name_rec*)malloc(sizeof(name_rec))) == NULL) exit(-1); + strncpy(name->boy_name,linebuf,BUFLEN); + HASH_ADD_STR(names,boy_name,name); + } + + fseek(file,0,SEEK_SET); + + while (fgets(linebuf,BUFLEN,file) != NULL) { + HASH_FIND_STR(names,linebuf,name); + if (!name) printf("failed to find: %s", linebuf); + else j++; + } + fclose(file); + printf("lookup on %d of %d names succeeded\n", j, i); + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.dat b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.dat new file mode 100644 index 0000000000..ea0b4eac6d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test14.dat @@ -0,0 +1,1219 @@ +JAMES +JOHN +ROBERT +MICHAEL +WILLIAM +DAVID +RICHARD +CHARLES +JOSEPH +THOMAS +CHRISTOPHER +DANIEL +PAUL +MARK +DONALD +GEORGE +KENNETH +STEVEN +EDWARD +BRIAN +RONALD +ANTHONY +KEVIN +JASON +MATTHEW +GARY +TIMOTHY +JOSE +LARRY +JEFFREY +FRANK +SCOTT +ERIC +STEPHEN +ANDREW +RAYMOND +GREGORY +JOSHUA +JERRY +DENNIS +WALTER +PATRICK +PETER +HAROLD +DOUGLAS +HENRY +CARL +ARTHUR +RYAN +ROGER +JOE +JUAN +JACK +ALBERT +JONATHAN +JUSTIN +TERRY +GERALD +KEITH +SAMUEL +WILLIE +RALPH +LAWRENCE +NICHOLAS +ROY +BENJAMIN +BRUCE +BRANDON +ADAM +HARRY +FRED +WAYNE +BILLY +STEVE +LOUIS +JEREMY +AARON +RANDY +HOWARD +EUGENE +CARLOS +RUSSELL +BOBBY +VICTOR +MARTIN +ERNEST +PHILLIP +TODD +JESSE +CRAIG +ALAN +SHAWN +CLARENCE +SEAN +PHILIP +CHRIS +JOHNNY +EARL +JIMMY +ANTONIO +DANNY +BRYAN +TONY +LUIS +MIKE +STANLEY +LEONARD +NATHAN +DALE +MANUEL +RODNEY +CURTIS +NORMAN +ALLEN +MARVIN +VINCENT +GLENN +JEFFERY +TRAVIS +JEFF +CHAD +JACOB +LEE +MELVIN +ALFRED +KYLE +FRANCIS +BRADLEY +JESUS +HERBERT +FREDERICK +RAY +JOEL +EDWIN +DON +EDDIE +RICKY +TROY +RANDALL +BARRY +ALEXANDER +BERNARD +MARIO +LEROY +FRANCISCO +MARCUS +MICHEAL +THEODORE +CLIFFORD +MIGUEL +OSCAR +JAY +JIM +TOM +CALVIN +ALEX +JON +RONNIE +BILL +LLOYD +TOMMY +LEON +DEREK +WARREN +DARRELL +JEROME +FLOYD +LEO +ALVIN +TIM +WESLEY +GORDON +DEAN +GREG +JORGE +DUSTIN +PEDRO +DERRICK +DAN +LEWIS +ZACHARY +COREY +HERMAN +MAURICE +VERNON +ROBERTO +CLYDE +GLEN +HECTOR +SHANE +RICARDO +SAM +RICK +LESTER +BRENT +RAMON +CHARLIE +TYLER +GILBERT +GENE +MARC +REGINALD +RUBEN +BRETT +ANGEL +NATHANIEL +RAFAEL +LESLIE +EDGAR +MILTON +RAUL +BEN +CHESTER +CECIL +DUANE +FRANKLIN +ANDRE +ELMER +BRAD +GABRIEL +RON +MITCHELL +ROLAND +ARNOLD +HARVEY +JARED +ADRIAN +KARL +CORY +CLAUDE +ERIK +DARRYL +JAMIE +NEIL +JESSIE +CHRISTIAN +JAVIER +FERNANDO +CLINTON +TED +MATHEW +TYRONE +DARREN +LONNIE +LANCE +CODY +JULIO +KELLY +KURT +ALLAN +NELSON +GUY +CLAYTON +HUGH +MAX +DWAYNE +DWIGHT +ARMANDO +FELIX +JIMMIE +EVERETT +JORDAN +IAN +WALLACE +KEN +BOB +JAIME +CASEY +ALFREDO +ALBERTO +DAVE +IVAN +JOHNNIE +SIDNEY +BYRON +JULIAN +ISAAC +MORRIS +CLIFTON +WILLARD +DARYL +ROSS +VIRGIL +ANDY +MARSHALL +SALVADOR +PERRY +KIRK +SERGIO +MARION +TRACY +SETH +KENT +TERRANCE +RENE +EDUARDO +TERRENCE +ENRIQUE +FREDDIE +WADE +AUSTIN +STUART +FREDRICK +ARTURO +ALEJANDRO +JACKIE +JOEY +NICK +LUTHER +WENDELL +JEREMIAH +EVAN +JULIUS +DANA +DONNIE +OTIS +SHANNON +TREVOR +OLIVER +LUKE +HOMER +GERARD +DOUG +KENNY +HUBERT +ANGELO +SHAUN +LYLE +MATT +LYNN +ALFONSO +ORLANDO +REX +CARLTON +ERNESTO +CAMERON +NEAL +PABLO +LORENZO +OMAR +WILBUR +BLAKE +GRANT +HORACE +RODERICK +KERRY +ABRAHAM +WILLIS +RICKEY +JEAN +IRA +ANDRES +CESAR +JOHNATHAN +MALCOLM +RUDOLPH +DAMON +KELVIN +RUDY +PRESTON +ALTON +ARCHIE +MARCO +WM +PETE +RANDOLPH +GARRY +GEOFFREY +JONATHON +FELIPE +BENNIE +GERARDO +ED +DOMINIC +ROBIN +LOREN +DELBERT +COLIN +GUILLERMO +EARNEST +LUCAS +BENNY +NOEL +SPENCER +RODOLFO +MYRON +EDMUND +GARRETT +SALVATORE +CEDRIC +LOWELL +GREGG +SHERMAN +WILSON +DEVIN +SYLVESTER +KIM +ROOSEVELT +ISRAEL +JERMAINE +FORREST +WILBERT +LELAND +SIMON +GUADALUPE +CLARK +IRVING +CARROLL +BRYANT +OWEN +RUFUS +WOODROW +SAMMY +KRISTOPHER +MACK +LEVI +MARCOS +GUSTAVO +JAKE +LIONEL +MARTY +TAYLOR +ELLIS +DALLAS +GILBERTO +CLINT +NICOLAS +LAURENCE +ISMAEL +ORVILLE +DREW +JODY +ERVIN +DEWEY +AL +WILFRED +JOSH +HUGO +IGNACIO +CALEB +TOMAS +SHELDON +ERICK +FRANKIE +STEWART +DOYLE +DARREL +ROGELIO +TERENCE +SANTIAGO +ALONZO +ELIAS +BERT +ELBERT +RAMIRO +CONRAD +PAT +NOAH +GRADY +PHIL +CORNELIUS +LAMAR +ROLANDO +CLAY +PERCY +DEXTER +BRADFORD +MERLE +DARIN +AMOS +TERRELL +MOSES +IRVIN +SAUL +ROMAN +DARNELL +RANDAL +TOMMIE +TIMMY +DARRIN +WINSTON +BRENDAN +TOBY +VAN +ABEL +DOMINICK +BOYD +COURTNEY +JAN +EMILIO +ELIJAH +CARY +DOMINGO +SANTOS +AUBREY +EMMETT +MARLON +EMANUEL +JERALD +EDMOND +EMIL +DEWAYNE +WILL +OTTO +TEDDY +REYNALDO +BRET +MORGAN +JESS +TRENT +HUMBERTO +EMMANUEL +STEPHAN +LOUIE +VICENTE +LAMONT +STACY +GARLAND +MILES +MICAH +EFRAIN +BILLIE +LOGAN +HEATH +RODGER +HARLEY +DEMETRIUS +ETHAN +ELDON +ROCKY +PIERRE +JUNIOR +FREDDY +ELI +BRYCE +ANTOINE +ROBBIE +KENDALL +ROYCE +STERLING +MICKEY +CHASE +GROVER +ELTON +CLEVELAND +DYLAN +CHUCK +DAMIAN +REUBEN +STAN +AUGUST +LEONARDO +JASPER +RUSSEL +ERWIN +BENITO +HANS +MONTE +BLAINE +ERNIE +CURT +QUENTIN +AGUSTIN +MURRAY +JAMAL +DEVON +ADOLFO +HARRISON +TYSON +BURTON +BRADY +ELLIOTT +WILFREDO +BART +JARROD +VANCE +DENIS +DAMIEN +JOAQUIN +HARLAN +DESMOND +ELLIOT +DARWIN +ASHLEY +GREGORIO +BUDDY +XAVIER +KERMIT +ROSCOE +ESTEBAN +ANTON +SOLOMON +SCOTTY +NORBERT +ELVIN +WILLIAMS +NOLAN +CAREY +ROD +QUINTON +HAL +BRAIN +ROB +ELWOOD +KENDRICK +DARIUS +MOISES +SON +MARLIN +FIDEL +THADDEUS +CLIFF +MARCEL +ALI +JACKSON +RAPHAEL +BRYON +ARMAND +ALVARO +JEFFRY +DANE +JOESPH +THURMAN +NED +SAMMIE +RUSTY +MICHEL +MONTY +RORY +FABIAN +REGGIE +MASON +GRAHAM +KRIS +ISAIAH +VAUGHN +GUS +AVERY +LOYD +DIEGO +ALEXIS +ADOLPH +NORRIS +MILLARD +ROCCO +GONZALO +DERICK +RODRIGO +GERRY +STACEY +CARMEN +WILEY +RIGOBERTO +ALPHONSO +TY +SHELBY +RICKIE +NOE +VERN +BOBBIE +REED +JEFFERSON +ELVIS +BERNARDO +MAURICIO +HIRAM +DONOVAN +BASIL +RILEY +OLLIE +NICKOLAS +MAYNARD +SCOT +VINCE +QUINCY +EDDY +SEBASTIAN +FEDERICO +ULYSSES +HERIBERTO +DONNELL +COLE +DENNY +DAVIS +GAVIN +EMERY +WARD +ROMEO +JAYSON +DION +DANTE +CLEMENT +COY +ODELL +MAXWELL +JARVIS +BRUNO +ISSAC +MARY +DUDLEY +BROCK +SANFORD +COLBY +CARMELO +BARNEY +NESTOR +HOLLIS +STEFAN +DONNY +ART +LINWOOD +BEAU +WELDON +GALEN +ISIDRO +TRUMAN +DELMAR +JOHNATHON +SILAS +FREDERIC +DICK +KIRBY +IRWIN +CRUZ +MERLIN +MERRILL +CHARLEY +MARCELINO +LANE +HARRIS +CLEO +CARLO +TRENTON +KURTIS +HUNTER +AURELIO +WINFRED +VITO +COLLIN +DENVER +CARTER +LEONEL +EMORY +PASQUALE +MOHAMMAD +MARIANO +DANIAL +BLAIR +LANDON +DIRK +BRANDEN +ADAN +NUMBERS +CLAIR +BUFORD +GERMAN +BERNIE +WILMER +JOAN +EMERSON +ZACHERY +FLETCHER +JACQUES +ERROL +DALTON +MONROE +JOSUE +DOMINIQUE +EDWARDO +BOOKER +WILFORD +SONNY +SHELTON +CARSON +THERON +RAYMUNDO +DAREN +TRISTAN +HOUSTON +ROBBY +LINCOLN +JAME +GENARO +GALE +BENNETT +OCTAVIO +CORNELL +LAVERNE +HUNG +ARRON +ANTONY +HERSCHEL +ALVA +GIOVANNI +GARTH +CYRUS +CYRIL +RONNY +STEVIE +LON +FREEMAN +ERIN +DUNCAN +KENNITH +CARMINE +AUGUSTINE +YOUNG +ERICH +CHADWICK +WILBURN +RUSS +REID +MYLES +ANDERSON +MORTON +JONAS +FOREST +MITCHEL +MERVIN +ZANE +RICH +JAMEL +LAZARO +ALPHONSE +RANDELL +MAJOR +JOHNIE +JARRETT +BROOKS +ARIEL +ABDUL +DUSTY +LUCIANO +LINDSEY +TRACEY +SEYMOUR +SCOTTIE +EUGENIO +MOHAMMED +SANDY +VALENTIN +CHANCE +ARNULFO +LUCIEN +FERDINAND +THAD +EZRA +SYDNEY +ALDO +RUBIN +ROYAL +MITCH +EARLE +ABE +WYATT +MARQUIS +LANNY +KAREEM +JAMAR +BORIS +ISIAH +EMILE +ELMO +ARON +LEOPOLDO +EVERETTE +JOSEF +GAIL +ELOY +DORIAN +RODRICK +REINALDO +LUCIO +JERROD +WESTON +HERSHEL +BARTON +PARKER +LEMUEL +LAVERN +BURT +JULES +GIL +ELISEO +AHMAD +NIGEL +EFREN +ANTWAN +ALDEN +MARGARITO +COLEMAN +REFUGIO +DINO +OSVALDO +LES +DEANDRE +NORMAND +KIETH +IVORY +ANDREA +TREY +NORBERTO +NAPOLEON +JEROLD +FRITZ +ROSENDO +MILFORD +SANG +DEON +CHRISTOPER +ALFONZO +LYMAN +JOSIAH +BRANT +WILTON +RICO +JAMAAL +DEWITT +CAROL +BRENTON +YONG +OLIN +FOSTER +FAUSTINO +CLAUDIO +JUDSON +GINO +EDGARDO +BERRY +ALEC +TANNER +JARRED +DONN +TRINIDAD +TAD +SHIRLEY +PRINCE +PORFIRIO +ODIS +MARIA +LENARD +CHAUNCEY +CHANG +TOD +MEL +MARCELO +KORY +AUGUSTUS +KEVEN +HILARIO +BUD +SAL +ROSARIO +ORVAL +MAURO +DANNIE +ZACHARIAH +OLEN +ANIBAL +MILO +JED +FRANCES +THANH +DILLON +AMADO +NEWTON +CONNIE +LENNY +TORY +RICHIE +LUPE +HORACIO +BRICE +MOHAMED +DELMER +DARIO +REYES +DEE +MAC +JONAH +JERROLD +ROBT +HANK +SUNG +RUPERT +ROLLAND +KENTON +DAMION +CHI +ANTONE +WALDO +FREDRIC +BRADLY +QUINN +KIP +BURL +WALKER +TYREE +JEFFEREY +AHMED +WILLY +STANFORD +OREN +NOBLE +MOSHE +MIKEL +ENOCH +BRENDON +QUINTIN +JAMISON +FLORENCIO +DARRICK +TOBIAS +MINH +HASSAN +GIUSEPPE +DEMARCUS +CLETUS +TYRELL +LYNDON +KEENAN +WERNER +THEO +GERALDO +LOU +COLUMBUS +CHET +BERTRAM +MARKUS +HUEY +HILTON +DWAIN +DONTE +TYRON +OMER +ISAIAS +HIPOLITO +FERMIN +CHUNG +ADALBERTO +VALENTINE +JAMEY +BO +BARRETT +WHITNEY +TEODORO +MCKINLEY +MAXIMO +GARFIELD +SOL +RALEIGH +LAWERENCE +ABRAM +RASHAD +KING +EMMITT +DARON +CHONG +SAMUAL +PARIS +OTHA +MIQUEL +LACY +EUSEBIO +DONG +DOMENIC +DARRON +BUSTER +ANTONIA +WILBER +RENATO +JC +HOYT +HAYWOOD +EZEKIEL +CHAS +FLORENTINO +ELROY +CLEMENTE +ARDEN +NEVILLE +KELLEY +EDISON +DESHAWN +CARROL +SHAYNE +NATHANIAL +JORDON +DANILO +CLAUD +VAL +SHERWOOD +RAYMON +RAYFORD +CRISTOBAL +AMBROSE +TITUS +HYMAN +FELTON +EZEQUIEL +ERASMO +STANTON +LONNY +LEN +IKE +MILAN +LINO +JAROD +HERB +ANDREAS +WALTON +RHETT +PALMER +JUDE +DOUGLASS +CORDELL +OSWALDO +ELLSWORTH +VIRGILIO +TONEY +NATHANAEL +DEL +BRITT +BENEDICT +MOSE +HONG +LEIGH +JOHNSON +ISREAL +GAYLE +GARRET +FAUSTO +ASA +ARLEN +ZACK +WARNER +MODESTO +FRANCESCO +MANUAL +JAE +GAYLORD +GASTON +FILIBERTO +DEANGELO +MICHALE +GRANVILLE +WES +MALIK +ZACKARY +TUAN +NICKY +ELDRIDGE +CRISTOPHER +CORTEZ +ANTIONE +MALCOM +LONG +KOREY +JOSPEH +COLTON +WAYLON +VON +HOSEA +SHAD +SANTO +RUDOLF +ROLF +REY +RENALDO +MARCELLUS +LUCIUS +LESLEY +KRISTOFER +BOYCE +BENTON +MAN +KASEY +JEWELL +HAYDEN +HARLAND +ARNOLDO +RUEBEN +LEANDRO +KRAIG +JERRELL +JEROMY +HOBERT +CEDRICK +ARLIE +WINFORD +WALLY +PATRICIA +LUIGI +KENETH +JACINTO +GRAIG +FRANKLYN +EDMUNDO +SID +PORTER +LEIF +LAUREN +JERAMY +ELISHA +BUCK +WILLIAN +VINCENZO +SHON +MICHAL +LYNWOOD +LINDSAY +JEWEL +JERE +HAI +ELDEN +DORSEY +DARELL +BRODERICK +ALONSO diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.ans new file mode 100644 index 0000000000..ad69a94709 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.ans @@ -0,0 +1 @@ +betty's id is 2 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.c new file mode 100644 index 0000000000..93a69f36a5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test15.c @@ -0,0 +1,34 @@ +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + char name[10]; /* key */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char **n, *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + int i=0; + + for (n = names; *n != NULL; n++) { + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + strncpy(s->name, *n,10); + s->id = i++; + HASH_ADD_STR( users, name, s ); + } + + HASH_FIND_STR( users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + /* free the hash table contents */ + HASH_ITER(hh, users, s, tmp) { + HASH_DEL(users, s); + free(s); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.ans new file mode 100644 index 0000000000..1b4d7e1835 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.ans @@ -0,0 +1 @@ +found: user 5, unix time 157680000 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.c new file mode 100644 index 0000000000..59b14f4f66 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test16.c @@ -0,0 +1,46 @@ +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* memset */ +#include "uthash.h" + +struct inner { + int a; + int b; +}; + +struct my_event { + struct inner is; /* key is aggregate of this field */ + char event_code; /* and this field. */ + int user_id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + struct my_event *e, ev, *events = NULL; + unsigned i, keylen; + + keylen = offsetof(struct my_event, event_code) + sizeof(char) + - offsetof(struct my_event, is); + + for(i = 0; i < 10; i++) { + e = (struct my_event*)malloc(sizeof(struct my_event)); + memset(e,0,sizeof(struct my_event)); + e->is.a = i * (60*60*24*365); /* i years (sec)*/ + e->is.b = 0; + e->event_code = 'a'+(i%2); /* meaningless */ + e->user_id = i; + + HASH_ADD( hh, events, is, keylen, e); + } + + /* look for one specific event */ + memset(&ev,0,sizeof(struct my_event)); + ev.is.a = 5 * (60*60*24*365); + ev.is.b = 0; + ev.event_code = 'b'; + HASH_FIND( hh, events, &ev.is, keylen , e); + if (e) printf("found: user %d, unix time %d\n", e->user_id, e->is.a); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.ans new file mode 100644 index 0000000000..92ae3ef251 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.ans @@ -0,0 +1,134 @@ +user 9, cookie 81 +user 8, cookie 64 +user 7, cookie 49 +user 6, cookie 36 +user 5, cookie 25 +user 4, cookie 16 +user 3, cookie 9 +user 2, cookie 4 +user 1, cookie 1 +user 0, cookie 0 +sorting +called for a:9, b:8 +called for a:7, b:6 +called for a:5, b:4 +called for a:3, b:2 +called for a:1, b:0 +called for a:8, b:6 +called for a:8, b:7 +called for a:4, b:2 +called for a:4, b:3 +called for a:6, b:2 +called for a:6, b:3 +called for a:6, b:4 +called for a:6, b:5 +called for a:2, b:0 +called for a:2, b:1 +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +adding 10-20 +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +user 20, cookie 400 +user 19, cookie 361 +user 18, cookie 324 +user 17, cookie 289 +user 16, cookie 256 +user 15, cookie 225 +user 14, cookie 196 +user 13, cookie 169 +user 12, cookie 144 +user 11, cookie 121 +user 10, cookie 100 +sorting +called for a:0, b:1 +called for a:2, b:3 +called for a:4, b:5 +called for a:6, b:7 +called for a:8, b:9 +called for a:20, b:19 +called for a:18, b:17 +called for a:16, b:15 +called for a:14, b:13 +called for a:12, b:11 +called for a:0, b:2 +called for a:1, b:2 +called for a:4, b:6 +called for a:5, b:6 +called for a:8, b:19 +called for a:9, b:19 +called for a:17, b:15 +called for a:17, b:16 +called for a:13, b:11 +called for a:13, b:12 +called for a:0, b:4 +called for a:1, b:4 +called for a:2, b:4 +called for a:3, b:4 +called for a:8, b:15 +called for a:9, b:15 +called for a:19, b:15 +called for a:19, b:16 +called for a:19, b:17 +called for a:19, b:18 +called for a:11, b:10 +called for a:0, b:8 +called for a:1, b:8 +called for a:2, b:8 +called for a:3, b:8 +called for a:4, b:8 +called for a:5, b:8 +called for a:6, b:8 +called for a:7, b:8 +called for a:0, b:10 +called for a:1, b:10 +called for a:2, b:10 +called for a:3, b:10 +called for a:4, b:10 +called for a:5, b:10 +called for a:6, b:10 +called for a:7, b:10 +called for a:8, b:10 +called for a:9, b:10 +called for a:15, b:10 +called for a:15, b:11 +called for a:15, b:12 +called for a:15, b:13 +called for a:15, b:14 +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +user 10, cookie 100 +user 11, cookie 121 +user 12, cookie 144 +user 13, cookie 169 +user 14, cookie 196 +user 15, cookie 225 +user 16, cookie 256 +user 17, cookie 289 +user 18, cookie 324 +user 19, cookie 361 +user 20, cookie 400 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.c new file mode 100644 index 0000000000..4684e451c5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test17.c @@ -0,0 +1,55 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int rev(void *_a, void *_b) { + example_user_t *a = (example_user_t*)_a; + example_user_t *b = (example_user_t*)_b; + printf("called for a:%d, b:%d\n",a->id, b->id); + return (a->id - b->id); +} + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL; + + /* create elements */ + for(i=9;i>=0;i--) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user != NULL; user=(example_user_t*)user->hh.next) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + printf("sorting\n"); + HASH_SORT(users,rev); + for(user=users; user != NULL; user=(example_user_t*)user->hh.next) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + + printf("adding 10-20\n"); + for(i=20;i>=10;i--) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + for(user=users; user != NULL; user=(example_user_t*)user->hh.next) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + printf("sorting\n"); + HASH_SORT(users,rev); + for(user=users; user != NULL; user=(example_user_t*)user->hh.next) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.ans new file mode 100644 index 0000000000..db48dcf103 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.ans @@ -0,0 +1,20 @@ +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +deleting id 0 +deleting id 1 +deleting id 2 +deleting id 3 +deleting id 4 +deleting id 5 +deleting id 6 +deleting id 7 +deleting id 8 +deleting id 9 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.c new file mode 100644 index 0000000000..80c4cf60bb --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test18.c @@ -0,0 +1,33 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user != NULL; user=(example_user_t*)user->hh.next) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + + /* delete them all, pathologically */ + while(users) { + printf("deleting id %i\n", users->id); + HASH_DEL(users,users); /* single head/deletee var! */ + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.ans new file mode 100644 index 0000000000..588d0d9a38 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.ans @@ -0,0 +1,1012 @@ +sorting users ascending +user 0 +user 1 +user 2 +user 3 +user 4 +user 5 +user 6 +user 7 +user 8 +user 9 +sorting altusers descending +altuser 999 +altuser 998 +altuser 997 +altuser 996 +altuser 995 +altuser 994 +altuser 993 +altuser 992 +altuser 991 +altuser 990 +altuser 989 +altuser 988 +altuser 987 +altuser 986 +altuser 985 +altuser 984 +altuser 983 +altuser 982 +altuser 981 +altuser 980 +altuser 979 +altuser 978 +altuser 977 +altuser 976 +altuser 975 +altuser 974 +altuser 973 +altuser 972 +altuser 971 +altuser 970 +altuser 969 +altuser 968 +altuser 967 +altuser 966 +altuser 965 +altuser 964 +altuser 963 +altuser 962 +altuser 961 +altuser 960 +altuser 959 +altuser 958 +altuser 957 +altuser 956 +altuser 955 +altuser 954 +altuser 953 +altuser 952 +altuser 951 +altuser 950 +altuser 949 +altuser 948 +altuser 947 +altuser 946 +altuser 945 +altuser 944 +altuser 943 +altuser 942 +altuser 941 +altuser 940 +altuser 939 +altuser 938 +altuser 937 +altuser 936 +altuser 935 +altuser 934 +altuser 933 +altuser 932 +altuser 931 +altuser 930 +altuser 929 +altuser 928 +altuser 927 +altuser 926 +altuser 925 +altuser 924 +altuser 923 +altuser 922 +altuser 921 +altuser 920 +altuser 919 +altuser 918 +altuser 917 +altuser 916 +altuser 915 +altuser 914 +altuser 913 +altuser 912 +altuser 911 +altuser 910 +altuser 909 +altuser 908 +altuser 907 +altuser 906 +altuser 905 +altuser 904 +altuser 903 +altuser 902 +altuser 901 +altuser 900 +altuser 899 +altuser 898 +altuser 897 +altuser 896 +altuser 895 +altuser 894 +altuser 893 +altuser 892 +altuser 891 +altuser 890 +altuser 889 +altuser 888 +altuser 887 +altuser 886 +altuser 885 +altuser 884 +altuser 883 +altuser 882 +altuser 881 +altuser 880 +altuser 879 +altuser 878 +altuser 877 +altuser 876 +altuser 875 +altuser 874 +altuser 873 +altuser 872 +altuser 871 +altuser 870 +altuser 869 +altuser 868 +altuser 867 +altuser 866 +altuser 865 +altuser 864 +altuser 863 +altuser 862 +altuser 861 +altuser 860 +altuser 859 +altuser 858 +altuser 857 +altuser 856 +altuser 855 +altuser 854 +altuser 853 +altuser 852 +altuser 851 +altuser 850 +altuser 849 +altuser 848 +altuser 847 +altuser 846 +altuser 845 +altuser 844 +altuser 843 +altuser 842 +altuser 841 +altuser 840 +altuser 839 +altuser 838 +altuser 837 +altuser 836 +altuser 835 +altuser 834 +altuser 833 +altuser 832 +altuser 831 +altuser 830 +altuser 829 +altuser 828 +altuser 827 +altuser 826 +altuser 825 +altuser 824 +altuser 823 +altuser 822 +altuser 821 +altuser 820 +altuser 819 +altuser 818 +altuser 817 +altuser 816 +altuser 815 +altuser 814 +altuser 813 +altuser 812 +altuser 811 +altuser 810 +altuser 809 +altuser 808 +altuser 807 +altuser 806 +altuser 805 +altuser 804 +altuser 803 +altuser 802 +altuser 801 +altuser 800 +altuser 799 +altuser 798 +altuser 797 +altuser 796 +altuser 795 +altuser 794 +altuser 793 +altuser 792 +altuser 791 +altuser 790 +altuser 789 +altuser 788 +altuser 787 +altuser 786 +altuser 785 +altuser 784 +altuser 783 +altuser 782 +altuser 781 +altuser 780 +altuser 779 +altuser 778 +altuser 777 +altuser 776 +altuser 775 +altuser 774 +altuser 773 +altuser 772 +altuser 771 +altuser 770 +altuser 769 +altuser 768 +altuser 767 +altuser 766 +altuser 765 +altuser 764 +altuser 763 +altuser 762 +altuser 761 +altuser 760 +altuser 759 +altuser 758 +altuser 757 +altuser 756 +altuser 755 +altuser 754 +altuser 753 +altuser 752 +altuser 751 +altuser 750 +altuser 749 +altuser 748 +altuser 747 +altuser 746 +altuser 745 +altuser 744 +altuser 743 +altuser 742 +altuser 741 +altuser 740 +altuser 739 +altuser 738 +altuser 737 +altuser 736 +altuser 735 +altuser 734 +altuser 733 +altuser 732 +altuser 731 +altuser 730 +altuser 729 +altuser 728 +altuser 727 +altuser 726 +altuser 725 +altuser 724 +altuser 723 +altuser 722 +altuser 721 +altuser 720 +altuser 719 +altuser 718 +altuser 717 +altuser 716 +altuser 715 +altuser 714 +altuser 713 +altuser 712 +altuser 711 +altuser 710 +altuser 709 +altuser 708 +altuser 707 +altuser 706 +altuser 705 +altuser 704 +altuser 703 +altuser 702 +altuser 701 +altuser 700 +altuser 699 +altuser 698 +altuser 697 +altuser 696 +altuser 695 +altuser 694 +altuser 693 +altuser 692 +altuser 691 +altuser 690 +altuser 689 +altuser 688 +altuser 687 +altuser 686 +altuser 685 +altuser 684 +altuser 683 +altuser 682 +altuser 681 +altuser 680 +altuser 679 +altuser 678 +altuser 677 +altuser 676 +altuser 675 +altuser 674 +altuser 673 +altuser 672 +altuser 671 +altuser 670 +altuser 669 +altuser 668 +altuser 667 +altuser 666 +altuser 665 +altuser 664 +altuser 663 +altuser 662 +altuser 661 +altuser 660 +altuser 659 +altuser 658 +altuser 657 +altuser 656 +altuser 655 +altuser 654 +altuser 653 +altuser 652 +altuser 651 +altuser 650 +altuser 649 +altuser 648 +altuser 647 +altuser 646 +altuser 645 +altuser 644 +altuser 643 +altuser 642 +altuser 641 +altuser 640 +altuser 639 +altuser 638 +altuser 637 +altuser 636 +altuser 635 +altuser 634 +altuser 633 +altuser 632 +altuser 631 +altuser 630 +altuser 629 +altuser 628 +altuser 627 +altuser 626 +altuser 625 +altuser 624 +altuser 623 +altuser 622 +altuser 621 +altuser 620 +altuser 619 +altuser 618 +altuser 617 +altuser 616 +altuser 615 +altuser 614 +altuser 613 +altuser 612 +altuser 611 +altuser 610 +altuser 609 +altuser 608 +altuser 607 +altuser 606 +altuser 605 +altuser 604 +altuser 603 +altuser 602 +altuser 601 +altuser 600 +altuser 599 +altuser 598 +altuser 597 +altuser 596 +altuser 595 +altuser 594 +altuser 593 +altuser 592 +altuser 591 +altuser 590 +altuser 589 +altuser 588 +altuser 587 +altuser 586 +altuser 585 +altuser 584 +altuser 583 +altuser 582 +altuser 581 +altuser 580 +altuser 579 +altuser 578 +altuser 577 +altuser 576 +altuser 575 +altuser 574 +altuser 573 +altuser 572 +altuser 571 +altuser 570 +altuser 569 +altuser 568 +altuser 567 +altuser 566 +altuser 565 +altuser 564 +altuser 563 +altuser 562 +altuser 561 +altuser 560 +altuser 559 +altuser 558 +altuser 557 +altuser 556 +altuser 555 +altuser 554 +altuser 553 +altuser 552 +altuser 551 +altuser 550 +altuser 549 +altuser 548 +altuser 547 +altuser 546 +altuser 545 +altuser 544 +altuser 543 +altuser 542 +altuser 541 +altuser 540 +altuser 539 +altuser 538 +altuser 537 +altuser 536 +altuser 535 +altuser 534 +altuser 533 +altuser 532 +altuser 531 +altuser 530 +altuser 529 +altuser 528 +altuser 527 +altuser 526 +altuser 525 +altuser 524 +altuser 523 +altuser 522 +altuser 521 +altuser 520 +altuser 519 +altuser 518 +altuser 517 +altuser 516 +altuser 515 +altuser 514 +altuser 513 +altuser 512 +altuser 511 +altuser 510 +altuser 509 +altuser 508 +altuser 507 +altuser 506 +altuser 505 +altuser 504 +altuser 503 +altuser 502 +altuser 501 +altuser 500 +altuser 499 +altuser 498 +altuser 497 +altuser 496 +altuser 495 +altuser 494 +altuser 493 +altuser 492 +altuser 491 +altuser 490 +altuser 489 +altuser 488 +altuser 487 +altuser 486 +altuser 485 +altuser 484 +altuser 483 +altuser 482 +altuser 481 +altuser 480 +altuser 479 +altuser 478 +altuser 477 +altuser 476 +altuser 475 +altuser 474 +altuser 473 +altuser 472 +altuser 471 +altuser 470 +altuser 469 +altuser 468 +altuser 467 +altuser 466 +altuser 465 +altuser 464 +altuser 463 +altuser 462 +altuser 461 +altuser 460 +altuser 459 +altuser 458 +altuser 457 +altuser 456 +altuser 455 +altuser 454 +altuser 453 +altuser 452 +altuser 451 +altuser 450 +altuser 449 +altuser 448 +altuser 447 +altuser 446 +altuser 445 +altuser 444 +altuser 443 +altuser 442 +altuser 441 +altuser 440 +altuser 439 +altuser 438 +altuser 437 +altuser 436 +altuser 435 +altuser 434 +altuser 433 +altuser 432 +altuser 431 +altuser 430 +altuser 429 +altuser 428 +altuser 427 +altuser 426 +altuser 425 +altuser 424 +altuser 423 +altuser 422 +altuser 421 +altuser 420 +altuser 419 +altuser 418 +altuser 417 +altuser 416 +altuser 415 +altuser 414 +altuser 413 +altuser 412 +altuser 411 +altuser 410 +altuser 409 +altuser 408 +altuser 407 +altuser 406 +altuser 405 +altuser 404 +altuser 403 +altuser 402 +altuser 401 +altuser 400 +altuser 399 +altuser 398 +altuser 397 +altuser 396 +altuser 395 +altuser 394 +altuser 393 +altuser 392 +altuser 391 +altuser 390 +altuser 389 +altuser 388 +altuser 387 +altuser 386 +altuser 385 +altuser 384 +altuser 383 +altuser 382 +altuser 381 +altuser 380 +altuser 379 +altuser 378 +altuser 377 +altuser 376 +altuser 375 +altuser 374 +altuser 373 +altuser 372 +altuser 371 +altuser 370 +altuser 369 +altuser 368 +altuser 367 +altuser 366 +altuser 365 +altuser 364 +altuser 363 +altuser 362 +altuser 361 +altuser 360 +altuser 359 +altuser 358 +altuser 357 +altuser 356 +altuser 355 +altuser 354 +altuser 353 +altuser 352 +altuser 351 +altuser 350 +altuser 349 +altuser 348 +altuser 347 +altuser 346 +altuser 345 +altuser 344 +altuser 343 +altuser 342 +altuser 341 +altuser 340 +altuser 339 +altuser 338 +altuser 337 +altuser 336 +altuser 335 +altuser 334 +altuser 333 +altuser 332 +altuser 331 +altuser 330 +altuser 329 +altuser 328 +altuser 327 +altuser 326 +altuser 325 +altuser 324 +altuser 323 +altuser 322 +altuser 321 +altuser 320 +altuser 319 +altuser 318 +altuser 317 +altuser 316 +altuser 315 +altuser 314 +altuser 313 +altuser 312 +altuser 311 +altuser 310 +altuser 309 +altuser 308 +altuser 307 +altuser 306 +altuser 305 +altuser 304 +altuser 303 +altuser 302 +altuser 301 +altuser 300 +altuser 299 +altuser 298 +altuser 297 +altuser 296 +altuser 295 +altuser 294 +altuser 293 +altuser 292 +altuser 291 +altuser 290 +altuser 289 +altuser 288 +altuser 287 +altuser 286 +altuser 285 +altuser 284 +altuser 283 +altuser 282 +altuser 281 +altuser 280 +altuser 279 +altuser 278 +altuser 277 +altuser 276 +altuser 275 +altuser 274 +altuser 273 +altuser 272 +altuser 271 +altuser 270 +altuser 269 +altuser 268 +altuser 267 +altuser 266 +altuser 265 +altuser 264 +altuser 263 +altuser 262 +altuser 261 +altuser 260 +altuser 259 +altuser 258 +altuser 257 +altuser 256 +altuser 255 +altuser 254 +altuser 253 +altuser 252 +altuser 251 +altuser 250 +altuser 249 +altuser 248 +altuser 247 +altuser 246 +altuser 245 +altuser 244 +altuser 243 +altuser 242 +altuser 241 +altuser 240 +altuser 239 +altuser 238 +altuser 237 +altuser 236 +altuser 235 +altuser 234 +altuser 233 +altuser 232 +altuser 231 +altuser 230 +altuser 229 +altuser 228 +altuser 227 +altuser 226 +altuser 225 +altuser 224 +altuser 223 +altuser 222 +altuser 221 +altuser 220 +altuser 219 +altuser 218 +altuser 217 +altuser 216 +altuser 215 +altuser 214 +altuser 213 +altuser 212 +altuser 211 +altuser 210 +altuser 209 +altuser 208 +altuser 207 +altuser 206 +altuser 205 +altuser 204 +altuser 203 +altuser 202 +altuser 201 +altuser 200 +altuser 199 +altuser 198 +altuser 197 +altuser 196 +altuser 195 +altuser 194 +altuser 193 +altuser 192 +altuser 191 +altuser 190 +altuser 189 +altuser 188 +altuser 187 +altuser 186 +altuser 185 +altuser 184 +altuser 183 +altuser 182 +altuser 181 +altuser 180 +altuser 179 +altuser 178 +altuser 177 +altuser 176 +altuser 175 +altuser 174 +altuser 173 +altuser 172 +altuser 171 +altuser 170 +altuser 169 +altuser 168 +altuser 167 +altuser 166 +altuser 165 +altuser 164 +altuser 163 +altuser 162 +altuser 161 +altuser 160 +altuser 159 +altuser 158 +altuser 157 +altuser 156 +altuser 155 +altuser 154 +altuser 153 +altuser 152 +altuser 151 +altuser 150 +altuser 149 +altuser 148 +altuser 147 +altuser 146 +altuser 145 +altuser 144 +altuser 143 +altuser 142 +altuser 141 +altuser 140 +altuser 139 +altuser 138 +altuser 137 +altuser 136 +altuser 135 +altuser 134 +altuser 133 +altuser 132 +altuser 131 +altuser 130 +altuser 129 +altuser 128 +altuser 127 +altuser 126 +altuser 125 +altuser 124 +altuser 123 +altuser 122 +altuser 121 +altuser 120 +altuser 119 +altuser 118 +altuser 117 +altuser 116 +altuser 115 +altuser 114 +altuser 113 +altuser 112 +altuser 111 +altuser 110 +altuser 109 +altuser 108 +altuser 107 +altuser 106 +altuser 105 +altuser 104 +altuser 103 +altuser 102 +altuser 101 +altuser 100 +altuser 99 +altuser 98 +altuser 97 +altuser 96 +altuser 95 +altuser 94 +altuser 93 +altuser 92 +altuser 91 +altuser 90 +altuser 89 +altuser 88 +altuser 87 +altuser 86 +altuser 85 +altuser 84 +altuser 83 +altuser 82 +altuser 81 +altuser 80 +altuser 79 +altuser 78 +altuser 77 +altuser 76 +altuser 75 +altuser 74 +altuser 73 +altuser 72 +altuser 71 +altuser 70 +altuser 69 +altuser 68 +altuser 67 +altuser 66 +altuser 65 +altuser 64 +altuser 63 +altuser 62 +altuser 61 +altuser 60 +altuser 59 +altuser 58 +altuser 57 +altuser 56 +altuser 55 +altuser 54 +altuser 53 +altuser 52 +altuser 51 +altuser 50 +altuser 49 +altuser 48 +altuser 47 +altuser 46 +altuser 45 +altuser 44 +altuser 43 +altuser 42 +altuser 41 +altuser 40 +altuser 39 +altuser 38 +altuser 37 +altuser 36 +altuser 35 +altuser 34 +altuser 33 +altuser 32 +altuser 31 +altuser 30 +altuser 29 +altuser 28 +altuser 27 +altuser 26 +altuser 25 +altuser 24 +altuser 23 +altuser 22 +altuser 21 +altuser 20 +altuser 19 +altuser 18 +altuser 17 +altuser 16 +altuser 15 +altuser 14 +altuser 13 +altuser 12 +altuser 11 +altuser 10 +altuser 9 +altuser 8 +altuser 7 +altuser 6 +altuser 5 +altuser 4 +altuser 3 +altuser 2 +altuser 1 +altuser 0 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.c new file mode 100644 index 0000000000..f0e26b65c9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test19.c @@ -0,0 +1,52 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; + UT_hash_handle alth; +} example_user_t; + +int ascending_sort(void *_a, void *_b) { + example_user_t *a = (example_user_t*)_a; + example_user_t *b = (example_user_t*)_b; + if (a->id == b->id) return 0; + return (a->id < b->id) ? -1 : 1; +} + +int descending_sort(void *_a, void *_b) { + example_user_t *a = (example_user_t*)_a; + example_user_t *b = (example_user_t*)_b; + if (a->id == b->id) return 0; + return (a->id < b->id) ? 1 : -1; +} + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL, *altusers=NULL; + + /* create elements */ + for(i=0;i<1000;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + if (i<10) HASH_ADD_INT(users,id,user); + HASH_ADD(alth,altusers,id,sizeof(int),user); + } + + printf("sorting users ascending\n"); + HASH_SRT(hh,users,ascending_sort); + for(user=users; user; user=(example_user_t*)user->hh.next) + printf("user %d\n", user->id); + + printf("sorting altusers descending\n"); + HASH_SRT(alth,altusers,descending_sort); + for(user=altusers; user; user=(example_user_t*)user->alth.next) + printf("altuser %d\n", user->id); + + /* HASH_FSCK(hh,users); */ + /* HASH_FSCK(alth,altusers); */ + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.ans new file mode 100644 index 0000000000..fd4e2f8ad6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.ans @@ -0,0 +1,5 @@ +user id 0 found, cookie 0 +user id 2 found, cookie 4 +user id 4 found, cookie 16 +user id 6 found, cookie 36 +user id 8 found, cookie 64 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.c new file mode 100644 index 0000000000..591ca79696 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test2.c @@ -0,0 +1,31 @@ +#include "uthash.h" +#include +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* find each even ID */ + for(i=0;i<10;i+=2) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) printf("user id %d found, cookie %d\n", tmp->id, tmp->cookie); + else printf("user id %d not found\n", i); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.ans new file mode 100644 index 0000000000..4d3bb1d0b6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.ans @@ -0,0 +1 @@ +found diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.c new file mode 100644 index 0000000000..152df39ebd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test20.c @@ -0,0 +1,28 @@ +#include /* memcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + char bkey[5]; /* "binary" key */ + int data; + UT_hash_handle hh; +}; + +int main(int argc, char *argv[]) { + struct my_struct *s, *t, *bins = NULL; + char binary[5] = {3,1,4,1,6}; + + /* allocate our structure. initialize to some values */ + s = (struct my_struct*)calloc(1,sizeof(struct my_struct)); + memcpy(s->bkey, binary, sizeof(binary)); + + /* add to hash table using general macro */ + HASH_ADD( hh, bins, bkey, sizeof(binary), s); + + /* look up the structure we just added */ + HASH_FIND( hh, bins, binary, sizeof(binary), t ); + + if (t) printf("found\n"); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.ans new file mode 100644 index 0000000000..af89f48c2d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.ans @@ -0,0 +1 @@ +found a 1 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.c new file mode 100644 index 0000000000..6043554de2 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test21.c @@ -0,0 +1,38 @@ +#include +#include +#include "uthash.h" + +typedef struct { + char a; + int b; +} record_key_t; + +typedef struct { + record_key_t key; + /* ... other data ... */ + UT_hash_handle hh; +} record_t; + +int main(int argc, char *argv[]) { + record_t l, *p, *r, *tmp, *records = NULL; + + r = (record_t*)malloc( sizeof(record_t) ); + memset(r, 0, sizeof(record_t)); + r->key.a = 'a'; + r->key.b = 1; + HASH_ADD(hh, records, key, sizeof(record_key_t), r); + + memset(&l, 0, sizeof(record_t)); + l.key.a = 'a'; + l.key.b = 1; + HASH_FIND(hh, records, &l.key, sizeof(record_key_t), p); + + if (p) printf("found %c %d\n", p->key.a, p->key.b); + + HASH_ITER(hh, records, p, tmp) { + HASH_DEL(records, p); + free(p); + } + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.ans new file mode 100644 index 0000000000..2483a24d61 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.ans @@ -0,0 +1 @@ +found diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.c new file mode 100644 index 0000000000..3435074028 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test22.c @@ -0,0 +1,59 @@ +#include /* malloc */ +#include /* offsetof */ +#include /* printf */ +#include /* memset */ +#include "uthash.h" + +#define UTF32 1 + +typedef struct { + UT_hash_handle hh; + int len; + char encoding; /* these two fields */ + int text[]; /* comprise the key */ +} msg_t; + +typedef struct { + char encoding; + int text[]; +} lookup_key_t; + +int main(int argc, char *argv[]) { + unsigned keylen; + msg_t *msg, *tmp, *msgs = NULL; + lookup_key_t *lookup_key; + + int beijing[] = {0x5317, 0x4eac}; /* UTF-32LE for 北京 */ + + /* allocate and initialize our structure */ + msg = (msg_t*)malloc( sizeof(msg_t) + sizeof(beijing) ); + memset(msg, 0, sizeof(msg_t)+sizeof(beijing)); /* zero fill */ + msg->len = sizeof(beijing); + msg->encoding = UTF32; + memcpy(msg->text, beijing, sizeof(beijing)); + + /* calculate the key length including padding, using formula */ + keylen = offsetof(msg_t, text) /* offset of last key field */ + + sizeof(beijing) /* size of last key field */ + - offsetof(msg_t, encoding); /* offset of first key field */ + + /* add our structure to the hash table */ + HASH_ADD( hh, msgs, encoding, keylen, msg); + + /* look it up to prove that it worked :-) */ + msg=NULL; + + lookup_key = (lookup_key_t*)malloc(sizeof(*lookup_key) + sizeof(beijing)); + memset(lookup_key, 0, sizeof(*lookup_key) + sizeof(beijing)); + lookup_key->encoding = UTF32; + memcpy(lookup_key->text, beijing, sizeof(beijing)); + HASH_FIND( hh, msgs, &lookup_key->encoding, keylen, msg ); + if (msg) printf("found \n"); + free(lookup_key); + + HASH_ITER(hh, msgs, msg, tmp) { + HASH_DEL(msgs, msg); + free(msg); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.ans new file mode 100644 index 0000000000..79ed10a1ac --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.ans @@ -0,0 +1,6 @@ +found 12345 +found 6789 +found 98765 +deleting 12345 +deleting 6789 +deleting 98765 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.c new file mode 100644 index 0000000000..78b633c774 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test23.c @@ -0,0 +1,44 @@ +#include +#include +#include "uthash.h" + +typedef struct { + int key; + int data; + UT_hash_handle hh; +} item; + +int main() { + item *i, *j, *items=NULL; + int k; + + /* first item */ + k = 12345; + i = (item*)malloc(sizeof(item)); + i->key = k; i->data = 0; + HASH_ADD_INT(items,key,i); + + /* second item */ + k = 6789; + i = (item*)malloc(sizeof(item)); + i->key = k; i->data = 0; + HASH_ADD_INT(items,key,i); + + /* third item */ + k = 98765; + i = (item*)malloc(sizeof(item)); + i->key = k; i->data = 0; + HASH_ADD_INT(items,key,i); + + /* look them all up */ + k = 12345; HASH_FIND_INT(items, &k, j); if (j) printf("found %d\n",k); + k = 6789; HASH_FIND_INT(items, &k, j); if (j) printf("found %d\n",k); + k = 98765; HASH_FIND_INT(items, &k, j); if (j) printf("found %d\n",k); + + /* delete them not the way we prefer but it works */ + for(j=items; j != NULL; j=(item*)j->hh.next) { + printf("deleting %d\n", j->key); + HASH_DEL(items,j); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.ans new file mode 100644 index 0000000000..4a2034ac8e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.ans @@ -0,0 +1 @@ +hash contains 10 items diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.c new file mode 100644 index 0000000000..28d3bf4047 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test24.c @@ -0,0 +1,25 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + printf("hash contains %d items\n", HASH_COUNT(users)); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.ans new file mode 100644 index 0000000000..5c71cdfdc2 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.ans @@ -0,0 +1,28 @@ +CDL macros +c b a +advancing head pointer +b a c +b a c b a c b a c b +b c a b c a b c a b +deleting b +a c +deleting (a) +c +deleting (c) + +DL macros +a b c +deleting tail c +a b +deleting head a +b +deleting head b + +LL macros +a b c +deleting tail c +a b +deleting head a +b +deleting head b + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.c new file mode 100644 index 0000000000..c22be85ec5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test25.c @@ -0,0 +1,112 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *head = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test CDL macros */ + printf("CDL macros\n"); + CDL_PREPEND(head,&els[0]); + CDL_PREPEND(head,&els[1]); + CDL_PREPEND(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* point head to head->next */ + printf("advancing head pointer\n"); + head = head->next; + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop a few times */ + for(i=0,e=head;e && i<10;i++,e=e->next) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop backwards a few times */ + for(i=0,e=head;e && i<10;i++,e=e->prev) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + CDL_DELETE(head,&els[1]); + CDL_FOREACH(head,e) printf("%c ", e->id); + printf("\n"); + printf("deleting (a)\n"); + CDL_DELETE(head,&els[0]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + printf("deleting (c)\n"); + CDL_DELETE(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test DL macros */ + printf("DL macros\n"); + DL_APPEND(head,&els[0]); + DL_APPEND(head,&els[1]); + DL_APPEND(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting tail c\n"); + DL_DELETE(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting head a\n"); + DL_DELETE(head,&els[0]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting head b\n"); + DL_DELETE(head,&els[1]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test LL macros */ + printf("LL macros\n"); + LL_APPEND(head,&els[0]); + LL_APPEND(head,&els[1]); + LL_APPEND(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting tail c\n"); + LL_DELETE(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting head a\n"); + LL_DELETE(head,&els[0]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting head b\n"); + LL_DELETE(head,&els[1]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.ans new file mode 100644 index 0000000000..764b01d2de --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.ans @@ -0,0 +1,53 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER +found WES + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.c new file mode 100644 index 0000000000..06277395c9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test26.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; /* important- initialize to NULL! */ + +int main(int argc, char *argv[]) { + el *name, *elt, *tmp, etmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + DL_APPEND(head, name); + } + DL_SORT(head, namecmp); + DL_FOREACH(head,elt) printf("%s", elt->bname); + + memcpy(&etmp.bname, "WES\n", 5); + DL_SEARCH(head,elt,&etmp,namecmp); + if (elt) printf("found %s\n", elt->bname); + + /* now delete each element, use the safe iterator */ + DL_FOREACH_SAFE(head,elt,tmp) { + DL_DELETE(head,elt); + } + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.ans new file mode 100644 index 0000000000..80b476c66e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.ans @@ -0,0 +1,28 @@ +CDL macros +c b a +advancing head pointer +b a c +b a c b a c b a c b +b c a b c a b c a b +deleting b +a c +deleting head (a) +c +deleting new head (c) + +DL macros +c b a +deleting c +b a +deleting a +b +deleting b + +LL macros +c b a +deleting c +b a +deleting a +b +deleting b + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.c new file mode 100644 index 0000000000..1b33e8ccb5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test27.c @@ -0,0 +1,112 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *head = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test CDL macros */ + printf("CDL macros\n"); + CDL_PREPEND(head,&els[0]); + CDL_PREPEND(head,&els[1]); + CDL_PREPEND(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* point head to head->next */ + printf("advancing head pointer\n"); + head = head->next; + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop a few times */ + for(i=0,e=head;e && i<10;i++,e=e->next) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop backwards a few times */ + for(i=0,e=head;e && i<10;i++,e=e->prev) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + CDL_DELETE(head,&els[1]); + CDL_FOREACH(head,e) printf("%c ", e->id); + printf("\n"); + printf("deleting head (a)\n"); + CDL_DELETE(head,&els[0]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + printf("deleting new head (c)\n"); + CDL_DELETE(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test DL macros */ + printf("DL macros\n"); + DL_PREPEND(head,&els[0]); + DL_PREPEND(head,&els[1]); + DL_PREPEND(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting c\n"); + DL_DELETE(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting a\n"); + DL_DELETE(head,&els[0]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + DL_DELETE(head,&els[1]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test LL macros */ + printf("LL macros\n"); + LL_PREPEND(head,&els[0]); + LL_PREPEND(head,&els[1]); + LL_PREPEND(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting c\n"); + LL_DELETE(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting a\n"); + LL_DELETE(head,&els[0]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + LL_DELETE(head,&els[1]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.ans new file mode 100644 index 0000000000..eb88650afe --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.ans @@ -0,0 +1,34 @@ +CDL macros +d c b a +advancing head pointer +c b a d +c b a d c b a d c b +c d a b c d a b c d +deleting b +c a d +deleting (a) +c d +deleting (c) +d +deleting (d) + +DL macros +c b a d +deleting c +b a d +deleting a +b d +deleting b +d +deleting d + +LL macros +c b a d +deleting c +b a d +deleting a +b d +deleting b +d +deleting d + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.c new file mode 100644 index 0000000000..279d891887 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test28.c @@ -0,0 +1,132 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *head = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test CDL macros */ + printf("CDL macros\n"); + CDL_PREPEND(head,&els[0]); + CDL_PREPEND(head,&els[1]); + CDL_PREPEND(head,&els[2]); + CDL_PREPEND(head,&els[3]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* point head to head->next */ + printf("advancing head pointer\n"); + head = head->next; + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop a few times */ + for(i=0,e=head;e && i<10;i++,e=e->next) + printf("%c ", e->id); + printf("\n"); + + /* follow circular loop backwards a few times */ + for(i=0,e=head;e && i<10;i++,e=e->prev) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + CDL_DELETE(head,&els[1]); + CDL_FOREACH(head,e) printf("%c ", e->id); + printf("\n"); + printf("deleting (a)\n"); + CDL_DELETE(head,&els[0]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + printf("deleting (c)\n"); + CDL_DELETE(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + printf("deleting (d)\n"); + CDL_DELETE(head,&els[3]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test DL macros */ + printf("DL macros\n"); + DL_PREPEND(head,&els[0]); + DL_PREPEND(head,&els[1]); + DL_PREPEND(head,&els[2]); + DL_APPEND(head,&els[3]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting c\n"); + DL_DELETE(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting a\n"); + DL_DELETE(head,&els[0]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + DL_DELETE(head,&els[1]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting d\n"); + DL_DELETE(head,&els[3]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + /* test LL macros */ + printf("LL macros\n"); + LL_PREPEND(head,&els[0]); + LL_PREPEND(head,&els[1]); + LL_PREPEND(head,&els[2]); + LL_APPEND(head,&els[3]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting c\n"); + LL_DELETE(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting a\n"); + LL_DELETE(head,&els[0]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting b\n"); + LL_DELETE(head,&els[1]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + printf("deleting d\n"); + LL_DELETE(head,&els[3]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.ans new file mode 100644 index 0000000000..6467b600ab --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.ans @@ -0,0 +1,103 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER +deleting head ADRIAN +head->prev: XAVIER +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.c new file mode 100644 index 0000000000..8e54a02c80 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test29.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + DL_APPEND(head, name); + } + DL_SORT(head, namecmp); + DL_FOREACH(head,tmp) printf("%s", tmp->bname); + + /* now delete the list head */ + printf("deleting head %shead->prev: %s", head->bname, head->prev->bname); + DL_DELETE(head,head); + DL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.ans new file mode 100644 index 0000000000..56a52a3f08 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.ans @@ -0,0 +1,5 @@ +user 1, cookie 1 +user 3, cookie 9 +user 5, cookie 25 +user 7, cookie 49 +user 9, cookie 81 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.c new file mode 100644 index 0000000000..3f3e479a11 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test3.c @@ -0,0 +1,38 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) + exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* delete each even ID */ + for(i=0;i<10;i+=2) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + HASH_DEL(users,tmp); + free(tmp); + } else printf("user id %d not found\n", i); + } + + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.ans new file mode 100644 index 0000000000..2b72e985a9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.ans @@ -0,0 +1,51 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.c new file mode 100644 index 0000000000..711c7d9950 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test30.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + CDL_PREPEND(head, name); + } + CDL_SORT(head, namecmp); + CDL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.ans new file mode 100644 index 0000000000..2b72e985a9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.ans @@ -0,0 +1,51 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.c new file mode 100644 index 0000000000..711c7d9950 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test31.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + CDL_PREPEND(head, name); + } + CDL_SORT(head, namecmp); + CDL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.ans new file mode 100644 index 0000000000..2a4d7f716a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.ans @@ -0,0 +1,51 @@ +ARNOLDO +COLTON +WES +WARNER +TONEY +NEVILLE +CHONG +KENTON +DARIO +DANNIE +ODIS +TRINIDAD +DONN +FRITZ +NORMAND +NIGEL +CORNELL +LINCOLN +RAYMUNDO +WINFRED +JARVIS +GUS +ISAIAH +XAVIER +CARY +ROLANDO +LAURENCE +CARROLL +IRVING +LOWELL +DAMON +OMAR +REX +ORLANDO +DOUG +WILLARD +CLIFTON +NELSON +CODY +ADRIAN +HARVEY +RON +LESTER +SHANE +WARREN +FREDERICK +GERALD +DOUGLAS +WALTER +WILLIAM +JOHN diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.c new file mode 100644 index 0000000000..fad2030268 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test32.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(el *a, el *b) { + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + DL_PREPEND(head, name); + } + /* DL_SORT(head, namecmp); */ + DL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.ans new file mode 100644 index 0000000000..2b72e985a9 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.ans @@ -0,0 +1,51 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.c new file mode 100644 index 0000000000..d946ec1f7b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test33.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + LL_PREPEND(head, name); + } + LL_SORT(head, namecmp); + LL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.ans new file mode 100644 index 0000000000..2a4d7f716a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.ans @@ -0,0 +1,51 @@ +ARNOLDO +COLTON +WES +WARNER +TONEY +NEVILLE +CHONG +KENTON +DARIO +DANNIE +ODIS +TRINIDAD +DONN +FRITZ +NORMAND +NIGEL +CORNELL +LINCOLN +RAYMUNDO +WINFRED +JARVIS +GUS +ISAIAH +XAVIER +CARY +ROLANDO +LAURENCE +CARROLL +IRVING +LOWELL +DAMON +OMAR +REX +ORLANDO +DOUG +WILLARD +CLIFTON +NELSON +CODY +ADRIAN +HARVEY +RON +LESTER +SHANE +WARREN +FREDERICK +GERALD +DOUGLAS +WALTER +WILLIAM +JOHN diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.c new file mode 100644 index 0000000000..653cdb8623 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test34.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "utlist.h" + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(el *a, el *b) { + return strcmp(a->bname,b->bname); +} + +el *head = NULL; + +int main(int argc, char *argv[]) { + el *name, *tmp; + + char linebuf[BUFLEN]; + FILE *file; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + CDL_PREPEND(head, name); + } + /* CDL_SORT(head, namecmp); */ + CDL_FOREACH(head,tmp) printf("%s", tmp->bname); + + fclose(file); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.ans new file mode 100644 index 0000000000..b33c8088ca --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.ans @@ -0,0 +1,30 @@ +0: aello +1: bello +2: cello +3: dello +4: eello +5: fello +6: gello +7: hello +8: iello +9: jello +found aello +right address? yes +found bello +right address? yes +found cello +right address? yes +found dello +right address? yes +found eello +right address? yes +found fello +right address? yes +found gello +right address? yes +found hello +right address? yes +found iello +right address? yes +found jello +right address? yes diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.c new file mode 100644 index 0000000000..6d41e5e854 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test35.c @@ -0,0 +1,37 @@ +#include "uthash.h" +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ + +typedef struct elt { + char *s; + UT_hash_handle hh; +} elt; + +int main(int argc,char *argv[]) { + int i; + elt *head = NULL; + elt elts[10]; + char label[6]; + for(i=0;i<10;i++) { + elts[i].s = (char*)malloc(6); + strncpy(elts[i].s, "hello",6); + elts[i].s[0] = 'a' + i; + printf("%d: %s\n", i, elts[i].s); + HASH_ADD_KEYPTR(hh, head, elts[i].s, 6, &elts[i]); + } + + /* look up each element and verify the result pointer */ + strncpy(label, "hello", 6); + for(i=0;i<10;i++) { + elt *e; + label[0] = 'a' + i; + HASH_FIND(hh,head,label,6,e); + if (e) { + printf( "found %s\n", e->s); + printf( "right address? %s\n", (e == &elts[i]) ? "yes" : "no"); + } + } + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.ans new file mode 100644 index 0000000000..3b5283b6c4 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.ans @@ -0,0 +1,15 @@ +user 0 +user 1 +user 2 +user 3 +user 4 +user 5 +user 6 +user 7 +user 8 +user 9 +auser 0 +auser 2 +auser 4 +auser 6 +auser 8 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.c new file mode 100644 index 0000000000..d91350b454 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test36.c @@ -0,0 +1,46 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct { + int id; + UT_hash_handle hh; + UT_hash_handle ah; +} example_user_t; + +#define EVENS(x) (((x)->id & 1) == 0) +int evens(void *userv) { + example_user_t *user = (example_user_t*)userv; + return ((user->id & 1) ? 0 : 1); +} + +int idcmp(void *_a, void *_b) { + example_user_t *a = (example_user_t*)_a; + example_user_t *b = (example_user_t*)_b; + return (a->id - b->id); +} + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL, *ausers=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + user = (example_user_t*)malloc(sizeof(example_user_t)); + user->id = i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user; user=(example_user_t*)(user->hh.next)) { + printf("user %d\n", user->id); + } + + /* now select some users into ausers */ + HASH_SELECT(ah,ausers,hh,users,evens); + HASH_SRT(ah,ausers,idcmp); + + for(user=ausers; user; user=(example_user_t*)(user->ah.next)) { + printf("auser %d\n", user->id); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.ans new file mode 100644 index 0000000000..f42a945cb4 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.ans @@ -0,0 +1,20 @@ +user 0 +user 1 +user 2 +user 3 +user 4 +user 5 +user 6 +user 7 +user 8 +user 9 +users count: 10 +auser 0 +auser 2 +auser 4 +auser 6 +auser 8 +ausers count: 5 +cleared ausers. +ausers count: 0 +users count: 10 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.c new file mode 100644 index 0000000000..7071e1c2ab --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test37.c @@ -0,0 +1,51 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct { + int id; + UT_hash_handle hh; + UT_hash_handle ah; +} example_user_t; + +#define EVENS(x) ((((example_user_t*)(x))->id & 1) == 0) + +int idcmp(void *_a, void *_b) { + example_user_t *a = (example_user_t*)_a; + example_user_t *b = (example_user_t*)_b; + return (a->id - b->id); +} + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL, *ausers=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + user = (example_user_t*)malloc(sizeof(example_user_t)); + user->id = i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user; user=(example_user_t*)(user->hh.next)) { + printf("user %d\n", user->id); + } + printf("users count: %u\n", HASH_CNT(hh,users)); + + /* now select some users into ausers */ + HASH_SELECT(ah,ausers,hh,users,EVENS); + HASH_SRT(ah,ausers,idcmp); + + for(user=ausers; user; user=(example_user_t*)(user->ah.next)) { + printf("auser %d\n", user->id); + } + printf("ausers count: %u\n", HASH_CNT(ah,ausers)); + HASH_CLEAR(ah,ausers); + printf("cleared ausers.\n"); + printf("ausers count: %u\n", HASH_CNT(ah,ausers)); + for(user=ausers; user; user=(example_user_t*)(user->ah.next)) { + printf("auser %d\n", user->id); + } + printf("users count: %u\n", HASH_CNT(hh,users)); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.ans new file mode 100644 index 0000000000..b20e04bcbb --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.ans @@ -0,0 +1 @@ +hash count 10 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.c new file mode 100644 index 0000000000..500d160e06 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test38.c @@ -0,0 +1,28 @@ +#include "uthash.h" +#include +#include + +struct test_t { + int a; + UT_hash_handle hh; +}; + +struct test_t *tests=NULL, *test; + +int main(void) { + int a, b; + for (b=0; b < 3; b++) { + for (a=0; a < 10; a++) { + test = NULL; + HASH_FIND(hh, tests, &a, sizeof(a), test); + if (! test) { + test = (struct test_t*)malloc(sizeof(struct test_t)); + memset(test, 0, sizeof(struct test_t)); + test->a = a; + HASH_ADD(hh, tests, a, sizeof(a), test); + } + } + } + printf("hash count %u\n", HASH_COUNT(tests)); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.ans new file mode 100644 index 0000000000..a7e7e6ceb1 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.ans @@ -0,0 +1,7 @@ +adding key eins +adding key zwei +adding key drei +hash count is 3 +looking for key eins... found. +looking for key zwei... found. +looking for key drei... found. diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.c new file mode 100644 index 0000000000..b0a5310185 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test39.c @@ -0,0 +1,31 @@ +#include +#include +#include "uthash.h" + +typedef struct { + const char *name; + UT_hash_handle hh; +} ns_t; + +ns_t *head = NULL; + +int main() { + const char *keys[] = {"eins", "zwei", "drei"}; + unsigned i; + ns_t *nsp; + + for(i=0; i < sizeof(keys)/sizeof(keys[0]); i++) { + printf("adding key %s\n", keys[i]); + nsp = (ns_t*)malloc(sizeof(ns_t)); + nsp->name = keys[i]; + HASH_ADD_KEYPTR(hh,head,nsp->name,strlen(nsp->name),nsp); + } + printf("hash count is %u\n", HASH_COUNT(head)); + + for(i=0; i < sizeof(keys)/sizeof(keys[0]); i++) { + printf("looking for key %s... ", keys[i]); + HASH_FIND(hh,head,keys[i],strlen(keys[i]),nsp); + printf("%s.\n", (nsp?"found":"not found")); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.ans new file mode 100644 index 0000000000..19b148f518 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.ans @@ -0,0 +1,10 @@ +cookie 0, user 0 +cookie 1, user 1 +cookie 4, user 2 +cookie 9, user 3 +cookie 16, user 4 +cookie 25, user 5 +cookie 36, user 6 +cookie 49, user 7 +cookie 64, user 8 +cookie 81, user 9 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.c new file mode 100644 index 0000000000..945c9aefb1 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test4.c @@ -0,0 +1,29 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; + UT_hash_handle alth; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *users=NULL, *altusers=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + HASH_ADD(alth,altusers,cookie,sizeof(int),user); + } + + for(user=altusers; user != NULL; user=(example_user_t*)(user->alth.next)) { + printf("cookie %d, user %d\n", user->cookie, user->id); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.ans new file mode 100644 index 0000000000..ad69a94709 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.ans @@ -0,0 +1 @@ +betty's id is 2 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.c new file mode 100644 index 0000000000..191c2f2905 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test40.c @@ -0,0 +1,34 @@ +#include /* strcpy */ +#include /* malloc */ +#include /* printf */ +#include "uthash.h" + +struct my_struct { + const char *name; /* key */ + int id; + UT_hash_handle hh; /* makes this structure hashable */ +}; + + +int main(int argc, char *argv[]) { + const char **n, *names[] = { "joe", "bob", "betty", NULL }; + struct my_struct *s, *tmp, *users = NULL; + int i=0; + + for (n = names; *n != NULL; n++) { + s = (struct my_struct*)malloc(sizeof(struct my_struct)); + s->name = *n; + s->id = i++; + HASH_ADD_KEYPTR( hh, users, s->name, strlen(s->name), s ); + } + + HASH_FIND_STR( users, "betty", s); + if (s) printf("betty's id is %d\n", s->id); + + /* free the hash table contents */ + HASH_ITER(hh, users, s, tmp) { + HASH_DEL(users, s); + free(s); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.ans new file mode 100644 index 0000000000..f19e34b764 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.ans @@ -0,0 +1,6 @@ +CDL macros +c b a deleting c deleting b deleting a +DL macros +a b c deleting a deleting b deleting c +LL macros +a b c deleting a deleting b deleting c diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.c new file mode 100644 index 0000000000..1e35443582 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test41.c @@ -0,0 +1,58 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *head = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e, *tmp, *tmp2; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test CDL macros */ + printf("CDL macros\n"); + CDL_PREPEND(head,&els[0]); + CDL_PREPEND(head,&els[1]); + CDL_PREPEND(head,&els[2]); + CDL_FOREACH(head,e) printf("%c ", e->id); + + /* point head to head->next */ + CDL_FOREACH_SAFE(head,e,tmp,tmp2) { + printf("deleting %c ", e->id); + CDL_DELETE(head,e); + } + printf("\n"); + if (head) printf("non-null head\n"); + + /* test DL macros */ + printf("DL macros\n"); + DL_APPEND(head,&els[0]); + DL_APPEND(head,&els[1]); + DL_APPEND(head,&els[2]); + DL_FOREACH(head,e) printf("%c ", e->id); + DL_FOREACH_SAFE(head,e,tmp) { + printf("deleting %c ", e->id); + DL_DELETE(head,e); + } + printf("\n"); + if (head) printf("non-null head\n"); + + /* test LL macros */ + printf("LL macros\n"); + LL_APPEND(head,&els[0]); + LL_APPEND(head,&els[1]); + LL_APPEND(head,&els[2]); + LL_FOREACH(head,e) printf("%c ", e->id); + LL_FOREACH_SAFE(head,e,tmp) { + printf("deleting %c ", e->id); + LL_DELETE(head,e); + } + printf("\n"); + if (head) printf("non-null head\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.ans new file mode 100644 index 0000000000..581660e3ff --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.ans @@ -0,0 +1,14 @@ +LL macros +a b c +search scalar found b +search found a + +DL macros +a b c +search scalar found b +search found a + +CDL macros +c b a +search scalar found b +search found a diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.c new file mode 100644 index 0000000000..b20c579f23 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test42.c @@ -0,0 +1,65 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *head = NULL; + +int eltcmp(el *a, el *b) {return a->id - b->id;} + +int main(int argc, char *argv[]) { + int i; + el els[10], *e, *tmp, *tmp2; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test LL macros */ + printf("LL macros\n"); + LL_APPEND(head,&els[0]); + LL_APPEND(head,&els[1]); + LL_APPEND(head,&els[2]); + LL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + LL_SEARCH_SCALAR(head, e, id, 'b'); + if (e) printf("search scalar found b\n"); + LL_SEARCH(head, e, &els[0], eltcmp); + if (e) printf("search found %c\n",e->id); + LL_FOREACH_SAFE(head,e,tmp) LL_DELETE(head,e); + + printf("\n"); + + /* test DL macros */ + printf("DL macros\n"); + DL_APPEND(head,&els[0]); + DL_APPEND(head,&els[1]); + DL_APPEND(head,&els[2]); + DL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + DL_SEARCH_SCALAR(head, e, id, 'b'); + if (e) printf("search scalar found b\n"); + DL_SEARCH(head, e, &els[0], eltcmp); + if (e) printf("search found %c\n",e->id); + DL_FOREACH_SAFE(head,e,tmp) DL_DELETE(head,e); + printf("\n"); + + /* test CDL macros */ + printf("CDL macros\n"); + CDL_PREPEND(head,&els[0]); + CDL_PREPEND(head,&els[1]); + CDL_PREPEND(head,&els[2]); + CDL_FOREACH(head,e) + printf("%c ", e->id); + printf("\n"); + CDL_SEARCH_SCALAR(head, e, id, 'b'); + if (e) printf("search scalar found b\n"); + CDL_SEARCH(head, e, &els[0], eltcmp); + if (e) printf("search found %c\n",e->id); + CDL_FOREACH_SAFE(head,e,tmp,tmp2) CDL_DELETE(head,e); + + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.ans new file mode 100644 index 0000000000..d09e9bd4ed --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.ans @@ -0,0 +1,88 @@ +length is 0 +push +length is 1 +back is 1 2 +pop +length is 0 +push +push +length is 2 +1 2 +3 4 +erase [0] +length is 1 +3 4 +push +3 4 +1 2 +clear +length is 0 +extend +length is 1 +ip points to [0] ? yes +push +0 0 +1 2 +erase [1] +length is 1 +0 0 +push +0 0 +3 4 +back is 3 4 +copy +cpy length is 2 +cpy 0 0 +cpy 3 4 +insert cpy[0] +cpy length is 3 +cpy 5 6 +cpy 0 0 +cpy 3 4 +erase cpy [0] [1] +cpy length is 1 +cpy 3 4 +inserta at cpy[1] +cpy length is 3 +cpy 3 4 +cpy 0 0 +cpy 3 4 +free cpy +length is 2 +resize to 30 +length is 30 +0 0 +3 4 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +0 0 +resize to 1 +length is 1 +resize to 0 +length is 0 +free diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.c new file mode 100644 index 0000000000..fbd40c8803 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test43.c @@ -0,0 +1,76 @@ +#include +#include "utarray.h" + +typedef struct { + int a; + int b; +} intpair_t; + +UT_icd pairicd = { sizeof(intpair_t),NULL,NULL,NULL}; + +int main() { + UT_array *pairs, *pairs_cpy; + intpair_t it, *ip; + size_t zero=0; + utarray_new(pairs, &pairicd); + printf("length is %d\n", utarray_len(pairs)); + it.a = 1; it.b=2; utarray_push_back(pairs, &it); printf("push\n"); + printf("length is %d\n", utarray_len(pairs)); + ip = (intpair_t*)utarray_back(pairs); + printf("back is %d %d\n", ip->a, ip->b); + utarray_pop_back(pairs); printf("pop\n"); + printf("length is %d\n", utarray_len(pairs)); + it.a = 1; it.b=2; utarray_push_back(pairs, &it); printf("push\n"); + it.a = 3; it.b=4; utarray_push_back(pairs, &it); printf("push\n"); + printf("length is %d\n", utarray_len(pairs)); + ip=NULL; + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + utarray_erase(pairs,0,1); printf("erase [0]\n"); + printf("length is %d\n", utarray_len(pairs)); + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + it.a = 1; it.b=2; utarray_push_back(pairs, &it); printf("push\n"); + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + utarray_clear(pairs); printf("clear\n"); + printf("length is %d\n", utarray_len(pairs)); + utarray_extend_back(pairs); printf("extend\n"); + ip = (intpair_t*)utarray_back(pairs); + printf("length is %d\n", utarray_len(pairs)); + printf("ip points to [0] ? %s\n", (ip==(intpair_t*)utarray_front(pairs)) ? "yes" : "no"); + it.a = 1; it.b=2; utarray_push_back(pairs, &it); printf("push\n"); + ip=NULL; + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + utarray_erase(pairs,1,1); printf("erase [1]\n"); + printf("length is %d\n", utarray_len(pairs)); + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + it.a = 3; it.b=4; utarray_push_back(pairs, &it); printf("push\n"); + for(ip=(intpair_t*)utarray_front(pairs);ip;ip=(intpair_t*)utarray_next(pairs,ip)) { + printf("%d %d\n", ip->a,ip->b); + } + ip = (intpair_t*)utarray_back(pairs); + printf("back is %d %d\n", ip->a, ip->b); + utarray_new(pairs_cpy, &pairicd); + utarray_concat(pairs_cpy, pairs); printf("copy\n"); + printf("cpy length is %d\n", utarray_len(pairs_cpy)); + ip=NULL; + while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip))) printf("cpy %d %d\n", ip->a, ip->b); + it.a=5; it.b=6; utarray_insert(pairs_cpy, &it, 0); printf("insert cpy[0]\n"); + printf("cpy length is %d\n", utarray_len(pairs_cpy)); + while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip))) printf("cpy %d %d\n", ip->a, ip->b); + utarray_erase(pairs_cpy,0,2); printf("erase cpy [0] [1]\n"); + printf("cpy length is %d\n", utarray_len(pairs_cpy)); + while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip))) printf("cpy %d %d\n", ip->a, ip->b); + utarray_inserta(pairs_cpy, pairs, 1); printf("inserta at cpy[1]\n"); + printf("cpy length is %d\n", utarray_len(pairs_cpy)); + while( (ip=(intpair_t*)utarray_next(pairs_cpy,ip))) printf("cpy %d %d\n", ip->a, ip->b); + utarray_free(pairs_cpy); printf("free cpy\n"); + printf("length is %d\n", utarray_len(pairs)); + utarray_resize(pairs, 30); printf("resize to 30\n"); + printf("length is %d\n", utarray_len(pairs)); + while( (ip=(intpair_t*)utarray_next(pairs,ip))) printf("%d %d\n", ip->a, ip->b); + utarray_resize(pairs, 1); printf("resize to 1\n"); + printf("length is %d\n", utarray_len(pairs)); + utarray_resize(pairs, zero); printf("resize to 0\n"); + printf("length is %d\n", utarray_len(pairs)); + utarray_free(pairs); printf("free\n"); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.ans new file mode 100644 index 0000000000..f771df8870 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.ans @@ -0,0 +1,9 @@ +0 1 2 3 4 5 6 7 8 9 +9 8 7 6 5 4 3 2 1 0 +9 8 7 3 2 1 0 +9 3 2 1 0 +3 2 1 0 +3 2 1 +3 2 1 0 0 +3 2 1 + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.c new file mode 100644 index 0000000000..6c419c2b06 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test44.c @@ -0,0 +1,44 @@ +#include +#include "utarray.h" + +int reverse(const void *a,const void*b) { + int _a = *(int*)a; + int _b = *(int*)b; + return _b - _a; +} + +int main() { + UT_array *a; + int i, *p; + utarray_new(a, &ut_int_icd); + for(i=0;i<10;i++) utarray_push_back(a,&i); + for(p=(int*)utarray_front(a); p; p=(int*)utarray_next(a,p)) printf("%d ",*p); + printf("\n"); + utarray_sort(a,reverse); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_erase(a,3,3); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_erase(a,1,2); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_erase(a,0,1); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_erase(a,3,1); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_resize(a,5); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_resize(a,3); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_erase(a,0,3); + while ( (p=(int*)utarray_next(a,p))) printf("%d ", *p); + printf("\n"); + utarray_free(a); + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.ans new file mode 100644 index 0000000000..7e744bc380 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.ans @@ -0,0 +1,3 @@ +1 2 3 4 5 6 7 8 +1 2 3 100 4 5 6 7 8 +1 2 3 100 4 5 6 7 8 1000 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.c new file mode 100644 index 0000000000..dca7d9b394 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test45.c @@ -0,0 +1,24 @@ +#include +#include "utarray.h" + +int main() { + UT_array *a; + int i, *p=NULL; + utarray_new(a, &ut_int_icd); + for(i=0;i<10;i++) utarray_push_back(a,&i); + utarray_pop_back(a); + utarray_erase(a,0,1); + while ( (p=(int*)utarray_next(a,p))) printf("%d ",*p); printf("\n"); + i = 100; + utarray_insert(a,&i,3); + while ( (p=(int*)utarray_next(a,p))) printf("%d ",*p); printf("\n"); + utarray_extend_back(a); + p = (int*)utarray_back(a); + *p = 1000; + p = NULL; + while ( (p=(int*)utarray_next(a,p))) printf("%d ",*p); printf("\n"); + utarray_clear(a); + utarray_free(a); + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.ans new file mode 100644 index 0000000000..be625f352c --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.ans @@ -0,0 +1,11 @@ +hello world +begin hello world +alt begin hello world oth +hello world oth +hello world +hello world begin hello world +begin hello world +sorting strs2 +begin hello world +reverse sorting strs2 +world hello begin diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.c new file mode 100644 index 0000000000..b7b785aade --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test46.c @@ -0,0 +1,49 @@ +#include +#include "utarray.h" + +int strsort(const void *_a, const void *_b) { + char *a = *(char**)_a; + char *b = *(char**)_b; + return strcmp(a,b); +} + +int revsort(const void *_a, const void *_b) { + char *a = *(char**)_a; + char *b = *(char**)_b; + return strcmp(b,a); +} + +int main() { + UT_array *strs,*strs2; + char *s, **p=NULL; + utarray_new(strs, &ut_str_icd); + s="hello"; utarray_push_back(strs, &s); + s="world"; utarray_push_back(strs, &s); + while ( (p=(char**)utarray_next(strs,p))) printf("%s ",*p); printf("\n"); + s="begin"; utarray_insert(strs,&s,0); + while ( (p=(char**)utarray_next(strs,p))) printf("%s ",*p); printf("\n"); + utarray_new(strs2, &ut_str_icd); + s="alt"; utarray_push_back(strs2, &s); + s="oth"; utarray_push_back(strs2, &s); + utarray_inserta(strs2, strs, 1); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + utarray_erase(strs2,0,2); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + utarray_pop_back(strs2); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + utarray_concat(strs2, strs); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + utarray_clear(strs2); + utarray_concat(strs2, strs); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + printf("sorting strs2\n"); + utarray_sort(strs2,strsort); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + printf("reverse sorting strs2\n"); + utarray_sort(strs2,revsort); + while ( (p=(char**)utarray_next(strs2,p))) printf("%s ",*p); printf("\n"); + utarray_clear(strs2); + utarray_free(strs2); + utarray_free(strs); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.ans new file mode 100644 index 0000000000..546e06442d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.ans @@ -0,0 +1,8 @@ +hello world +hello world text + second +hello world text second +cleared, length t now: 0 +length s now: 23 +one 1 two 2 three (3) +length t now: 21 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.c new file mode 100644 index 0000000000..1434d5a679 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test47.c @@ -0,0 +1,28 @@ +#include /* printf */ +#include "utstring.h" + +int main() { + UT_string *s,*t; + char a[] = " text"; + utstring_new(s); + utstring_new(t); + + utstring_printf(s,"hello %s", "world"); + printf("%s\n", utstring_body(s)); + utstring_bincpy(s,a,sizeof(a)-1); + printf("%s\n", utstring_body(s)); + utstring_printf(t," second"); + printf("%s\n", utstring_body(t)); + utstring_concat(s,t); + printf("%s\n", utstring_body(s)); + utstring_clear(t); + printf("cleared, length t now: %d\n", utstring_len(t)); + printf("length s now: %d\n", utstring_len(s)); + utstring_printf(t,"one %d two %u three %s", 1, 2, "(3)"); + printf("%s\n", utstring_body(t)); + printf("length t now: %d\n", utstring_len(t)); + + + utstring_free(s); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.ans new file mode 100644 index 0000000000..8b1acc12b6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.ans @@ -0,0 +1,10 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.c new file mode 100644 index 0000000000..dd0fa75a6b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test48.c @@ -0,0 +1,20 @@ +#include +#include "utarray.h" + +int main() { + UT_array *nums; + int i, *p; + + utarray_new(nums,&ut_int_icd); + for(i=0; i < 10; i++) utarray_push_back(nums,&i); + + for(p=(int*)utarray_front(nums); + p!=NULL; + p=(int*)utarray_next(nums,p)) { + printf("%d\n",*p); + } + + utarray_free(nums); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.ans new file mode 100644 index 0000000000..94954abda4 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.ans @@ -0,0 +1,2 @@ +hello +world diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.c new file mode 100644 index 0000000000..00afb7abfc --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test49.c @@ -0,0 +1,20 @@ +#include +#include "utarray.h" + +int main() { + UT_array *strs; + char *s, **p; + + utarray_new(strs,&ut_str_icd); + + s = "hello"; utarray_push_back(strs, &s); + s = "world"; utarray_push_back(strs, &s); + p = NULL; + while ( (p=(char**)utarray_next(strs,p))) { + printf("%s\n",*p); + } + + utarray_free(strs); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.ans new file mode 100644 index 0000000000..2ece88fe50 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.ans @@ -0,0 +1,5 @@ +cookie 0 found, user id 0 +cookie 4 found, user id 2 +cookie 16 found, user id 4 +cookie 36 found, user id 6 +cookie 64 found, user id 8 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.c new file mode 100644 index 0000000000..7cfd278eab --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test5.c @@ -0,0 +1,33 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; + UT_hash_handle alth; +} example_user_t; + +int main(int argc,char *argv[]) { + int i,j; + example_user_t *user, *tmp, *users=NULL, *altusers=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + HASH_ADD(alth,altusers,cookie,sizeof(int),user); + } + + /* find cookie corresponding to each even ID */ + for(i=0;i<10;i+=2) { + j=i*i; + HASH_FIND(alth,altusers,&j,sizeof(int),tmp); + if (tmp) printf("cookie %d found, user id %d\n", tmp->cookie, tmp->id); + else printf("cookie %d not found\n", j); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.ans new file mode 100644 index 0000000000..1191247b6d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.ans @@ -0,0 +1,2 @@ +1 +2 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.c new file mode 100644 index 0000000000..0ef78d7a8b --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test50.c @@ -0,0 +1,19 @@ +#include +#include "utarray.h" + +UT_icd long_icd = {sizeof(long), NULL, NULL, NULL }; + +int main() { + UT_array *nums; + long l, *p; + utarray_new(nums, &long_icd); + + l=1; utarray_push_back(nums, &l); + l=2; utarray_push_back(nums, &l); + + p=NULL; + while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p); + + utarray_free(nums); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.ans new file mode 100644 index 0000000000..3caf269762 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.ans @@ -0,0 +1,2 @@ +1 2 +10 20 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.c new file mode 100644 index 0000000000..48df2e0ecf --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test51.c @@ -0,0 +1,28 @@ +#include +#include "utarray.h" + +typedef struct { + int a; + int b; +} intpair_t; + +UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL}; + +int main() { + + UT_array *pairs; + intpair_t ip, *p; + utarray_new(pairs,&intpair_icd); + + ip.a=1; ip.b=2; utarray_push_back(pairs, &ip); + ip.a=10; ip.b=20; utarray_push_back(pairs, &ip); + + for(p=(intpair_t*)utarray_front(pairs); + p!=NULL; + p=(intpair_t*)utarray_next(pairs,p)) { + printf("%d %d\n", p->a, p->b); + } + + utarray_free(pairs); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.ans new file mode 100644 index 0000000000..bd37407180 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.ans @@ -0,0 +1,2 @@ +1 hello +2 world diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.c new file mode 100644 index 0000000000..b4b3465120 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test52.c @@ -0,0 +1,39 @@ +#include +#include +#include "utarray.h" + +typedef struct { + int a; + char *s; +} intchar_t; + +void intchar_copy(void *_dst, const void *_src) { + intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src; + dst->a = src->a; + dst->s = src->s ? strdup(src->s) : NULL; +} + +void intchar_dtor(void *_elt) { + intchar_t *elt = (intchar_t*)_elt; + if (elt->s) free(elt->s); +} + +UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor}; + +int main() { + UT_array *intchars; + intchar_t ic, *p; + utarray_new(intchars, &intchar_icd); + + ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic); + ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic); + + p=NULL; + while( (p=(intchar_t*)utarray_next(intchars,p))) { + printf("%d %s\n", p->a, (p->s ? p->s : "null")); + } + + utarray_free(intchars); + return 0; +} + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.ans new file mode 100644 index 0000000000..a042389697 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.ans @@ -0,0 +1 @@ +hello world! diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.c new file mode 100644 index 0000000000..b8fafc5927 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test53.c @@ -0,0 +1,13 @@ +#include +#include "utstring.h" + +int main() { + UT_string *s; + + utstring_new(s); + utstring_printf(s, "hello world!" ); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.ans new file mode 100644 index 0000000000..df0083e393 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.ans @@ -0,0 +1,2 @@ +length: 21 +hello world hi there diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.c new file mode 100644 index 0000000000..8774bc544e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test54.c @@ -0,0 +1,23 @@ +#include +#include "utstring.h" + +int main() { + UT_string *s, *t; + + utstring_new(s); + utstring_new(t); + + utstring_printf(s, "hello " ); + utstring_printf(s, "world " ); + + utstring_printf(t, "hi " ); + utstring_printf(t, "there " ); + + utstring_concat(s, t); + printf("length: %u\n", utstring_len(s)); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + utstring_free(t); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.ans new file mode 100644 index 0000000000..a75e7be259 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.ans @@ -0,0 +1,2 @@ +length is 3 +number 10 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.c new file mode 100644 index 0000000000..e421d681d4 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test55.c @@ -0,0 +1,18 @@ +#include +#include "utstring.h" + +int main() { + UT_string *s; + char binary[] = "\xff\xff"; + + utstring_new(s); + utstring_bincpy(s, binary, sizeof(binary)); + printf("length is %u\n", utstring_len(s)); + + utstring_clear(s); + utstring_printf(s,"number %d", 10); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.ans new file mode 100644 index 0000000000..f615d0072a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.ans @@ -0,0 +1,65 @@ +ADRIAN +ARNOLDO +CARROLL +CARY +CHONG +CLIFTON +CODY +COLTON +CORNELL +DAMON +DANNIE +DARIO +DONN +DOUG +DOUGLAS +FREDERICK +FRITZ +GERALD +GUS +HARVEY +IRVING +ISAIAH +JARVIS +JOHN +KENTON +LAURENCE +LESTER +LINCOLN +LOWELL +NELSON +NEVILLE +NIGEL +NORMAND +ODIS +OMAR +ORLANDO +RAYMUNDO +REX +ROLANDO +RON +SHANE +TONEY +TRINIDAD +WALTER +WARNER +WARREN +WES +WILLARD +WILLIAM +WINFRED +XAVIER +found WES + +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +length is 3 +number 10 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.c new file mode 100644 index 0000000000..88c015c46a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test56.c @@ -0,0 +1,86 @@ +#include /* malloc */ +#include /* printf */ +#include +#include "uthash.h" +#include "utlist.h" +#include "utstring.h" + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +#define BUFLEN 20 + +typedef struct el { + char bname[BUFLEN]; + struct el *next, *prev; +} el; + +int namecmp(void *_a, void *_b) { + el *a = (el*)_a; + el *b = (el*)_b; + return strcmp(a->bname,b->bname); +} + +el *head = NULL; /* important- initialize to NULL! */ + +int main(int argc, char *argv[]) { + el *name, *elt, *tmp, etmp; + int i; + example_user_t *user, *users=NULL; + + char linebuf[BUFLEN]; + FILE *file; + + UT_string *s; + char binary[] = "\xff\xff"; + + if ( (file = fopen( "test11.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,BUFLEN,file) != NULL) { + if ( (name = (el*)malloc(sizeof(el))) == NULL) exit(-1); + strncpy(name->bname,linebuf,BUFLEN); + DL_APPEND(head, name); + } + DL_SORT(head, namecmp); + DL_FOREACH(head,elt) printf("%s", elt->bname); + + memcpy(&etmp.bname, "WES\n", 5); + DL_SEARCH(head,elt,&etmp,namecmp); + if (elt) printf("found %s\n", elt->bname); + + /* now delete each element, use the safe iterator */ + DL_FOREACH_SAFE(head,elt,tmp) { + DL_DELETE(head,elt); + } + + fclose(file); + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + + utstring_new(s); + utstring_bincpy(s, binary, sizeof(binary)); + printf("length is %u\n", utstring_len(s)); + + utstring_clear(s); + utstring_printf(s,"number %d", 10); + printf("%s\n", utstring_body(s)); + + utstring_free(s); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.ans new file mode 100644 index 0000000000..4d3bb1d0b6 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.ans @@ -0,0 +1 @@ +found diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.c new file mode 100644 index 0000000000..6a96e27025 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test57.c @@ -0,0 +1,28 @@ +#include +#include +#include "uthash.h" + +typedef struct { + void *key; + int i; + UT_hash_handle hh; +} el_t; + +el_t *hash = NULL; +char *someaddr = NULL; + +int main() { + el_t *d; + el_t *e = (el_t*)malloc(sizeof(el_t)); + if (!e) return -1; + e->key = (void*)someaddr; + e->i = 1; + HASH_ADD_PTR(hash,key,e); + HASH_FIND_PTR(hash, &someaddr, d); + if (d) printf("found\n"); + + /* release memory */ + HASH_DEL(hash,e); + free(e); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.ans new file mode 100644 index 0000000000..a23ca72bc5 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.ans @@ -0,0 +1,18 @@ +user 0, cookie 0 +user 1, cookie 1 +user 2, cookie 4 +user 3, cookie 9 +user 4, cookie 16 +user 5, cookie 25 +user 6, cookie 36 +user 7, cookie 49 +user 8, cookie 64 +user 9, cookie 81 +10 users. Deleting odd id's... +user 0, cookie 0 +user 2, cookie 4 +user 4, cookie 16 +user 6, cookie 36 +user 8, cookie 64 +5 users. Deleting remaining id's... +0 users. diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.c new file mode 100644 index 0000000000..6ec891c954 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test58.c @@ -0,0 +1,55 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i,c; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) + exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + + + c = HASH_COUNT(users); + printf("%d users. Deleting odd id's...\n", c); + /* delete the odd id's */ + HASH_ITER(hh, users, user, tmp) { + if (user->id & 1) HASH_DEL(users,user); + } + + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + + c = HASH_COUNT(users); + printf("%d users. Deleting remaining id's...\n", c); + /* delete all that are left */ + HASH_ITER(hh, users, user, tmp) { + HASH_DEL(users,user); + } + + c = HASH_COUNT(users); + printf("%d users.\n", c); + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.ans new file mode 100644 index 0000000000..e19f2f6cdf --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.ans @@ -0,0 +1 @@ +$items{bob}{age} = 37 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.c new file mode 100644 index 0000000000..1fab25e8ff --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test59.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include "uthash.h" + +/* hash of hashes */ +typedef struct item { + char name[10]; + struct item *sub; + int val; + UT_hash_handle hh; +} item_t; + +item_t *items=NULL; + +int main(int argc, char *argvp[]) { + item_t *item1, *item2, *tmp1, *tmp2; + + /* make initial element */ + item_t *i = malloc(sizeof(*i)); + strcpy(i->name, "bob"); + i->sub = NULL; + i->val = 0; + HASH_ADD_STR(items, name, i); + + /* add a sub hash table off this element */ + item_t *s = malloc(sizeof(*s)); + strcpy(s->name, "age"); + s->sub = NULL; + s->val = 37; + HASH_ADD_STR(i->sub, name, s); + + /* iterate over hash elements */ + HASH_ITER(hh, items, item1, tmp1) { + HASH_ITER(hh, item1->sub, item2, tmp2) { + printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val); + } + } + + /* clean up both hash tables */ + HASH_ITER(hh, items, item1, tmp1) { + HASH_ITER(hh, item1->sub, item2, tmp2) { + HASH_DEL(item1->sub, item2); + free(item2); + } + HASH_DEL(items, item1); + free(item1); + } + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.ans new file mode 100644 index 0000000000..5c95811c11 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.ans @@ -0,0 +1 @@ +alt malloc table diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.c new file mode 100644 index 0000000000..40fc2aad92 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test6.c @@ -0,0 +1,52 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +/* Set up macros for alternative malloc/free functions */ +#undef uthash_malloc +#undef uthash_free +#define uthash_malloc(sz) alt_malloc(sz) +#define uthash_free(ptr,sz) alt_free(ptr) + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +void *alt_malloc(size_t sz) { + if (sz == sizeof(UT_hash_table)) printf("%s\n", "alt malloc table"); + return malloc(sz); +} +void alt_free(void *ptr) { + /* printf("%s\n", "alt_free"); */ + free(ptr); +} + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* delete each ID */ + for(i=0;i<10;i++) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + HASH_DEL(users,tmp); + free(tmp); + } else printf("user id %d not found\n", i); + } + + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.ans new file mode 100644 index 0000000000..e19f2f6cdf --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.ans @@ -0,0 +1 @@ +$items{bob}{age} = 37 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.c new file mode 100644 index 0000000000..a1c1486c9d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test60.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include "uthash.h" + +/* hash of hashes */ +typedef struct item { + char name[10]; + struct item *sub; + int val; + UT_hash_handle hh; +} item_t; + +item_t *items=NULL; + +int main(int argc, char *argvp[]) { + item_t *item1, *item2, *tmp1, *tmp2; + + /* make initial element */ + item_t *i = malloc(sizeof(*i)); + strcpy(i->name, "bob"); + i->sub = NULL; + i->val = 0; + HASH_ADD_STR(items, name, i); + + /* add a sub hash table off this element */ + item_t *s = malloc(sizeof(*s)); + strcpy(s->name, "age"); + s->sub = NULL; + s->val = 37; + HASH_ADD_STR(i->sub, name, s); + + /* iterate over hash elements, printing and freeing them */ + HASH_ITER(hh, items, item1, tmp1) { + HASH_ITER(hh, item1->sub, item2, tmp2) { + printf("$items{%s}{%s} = %d\n", item1->name, item2->name, item2->val); + HASH_DEL(item1->sub, item2); + free(item2); + } + HASH_DEL(items, item1); + free(item1); + } + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.ans new file mode 100644 index 0000000000..39c63b45d3 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.ans @@ -0,0 +1,16 @@ +hello +world +one +two +three +sorting +finding hello + hello +finding one + one +finding three + three +finding two + two +finding world + world diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.c new file mode 100644 index 0000000000..b569957e62 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test61.c @@ -0,0 +1,42 @@ +#include +#include "utarray.h" + +int strsort(const void *_a, const void *_b) { + char *a = *(char**)_a; + char *b = *(char**)_b; + return strcmp(a,b); +} + +int main() { + UT_array *strs; + char *s, **p; + + utarray_new(strs,&ut_str_icd); + + s = "hello"; utarray_push_back(strs, &s); + s = "world"; utarray_push_back(strs, &s); + s = "one"; utarray_push_back(strs, &s); + s = "two"; utarray_push_back(strs, &s); + s = "three"; utarray_push_back(strs, &s); + + p = NULL; + while ( (p=(char**)utarray_next(strs,p))) { + s = *p; + printf("%s\n",s); + } + + printf("sorting\n"); + utarray_sort(strs,strsort); + + p = NULL; + while ( (p=(char**)utarray_next(strs,p))) { + s = *p; + printf("finding %s\n",s); + p = utarray_find(strs,&s,strsort); + printf(" %s\n", p ? (*p) : "failed"); + } + + utarray_free(strs); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.ans new file mode 100644 index 0000000000..1b71740f6d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.ans @@ -0,0 +1,20 @@ +al aligned (y): y +u1 aligned (n): n +u2 aligned (n): n +u3 aligned (n): n + +al plus1 (n): n +u1 plus1 (y): y +u2 plus1 (n): n +u3 plus1 (n): n + +al plus2 (n): n +u1 plus2 (n): n +u2 plus2 (y): y +u3 plus2 (n): n + +al plus3 (n): n +u1 plus3 (n): n +u2 plus3 (n): n +u3 plus3 (y): y + diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.c new file mode 100644 index 0000000000..ae80b9e32a --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test62.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include "uthash.h" + +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) + +#define yn(rc) (rc?"y":"n") +int main(int argc,char*argv[]) { + unsigned rc; + char *c = malloc(8); + *(c+0) = 0x00; unsigned *al = (unsigned*)(c+0); + *(c+1) = 0x01; unsigned *u1 = (unsigned*)(c+1); + *(c+2) = 0x02; unsigned *u2 = (unsigned*)(c+2); + *(c+3) = 0x03; unsigned *u3 = (unsigned*)(c+3); + *(c+4) = 0x04; + *(c+5) = 0x05; + *(c+6) = 0x06; + *(c+7) = 0x07; + + /* ---------------------------------------- */ + /* test whether alignment is detected properly */ + + rc = MUR_PLUS0_ALIGNED(al); printf("al aligned (y): %s\n", yn(rc)); + rc = MUR_PLUS0_ALIGNED(u1); printf("u1 aligned (n): %s\n", yn(rc)); + rc = MUR_PLUS0_ALIGNED(u2); printf("u2 aligned (n): %s\n", yn(rc)); + rc = MUR_PLUS0_ALIGNED(u3); printf("u3 aligned (n): %s\n", yn(rc)); + printf("\n"); + + rc = MUR_PLUS1_ALIGNED(al); printf("al plus1 (n): %s\n", yn(rc)); + rc = MUR_PLUS1_ALIGNED(u1); printf("u1 plus1 (y): %s\n", yn(rc)); + rc = MUR_PLUS1_ALIGNED(u2); printf("u2 plus1 (n): %s\n", yn(rc)); + rc = MUR_PLUS1_ALIGNED(u3); printf("u3 plus1 (n): %s\n", yn(rc)); + printf("\n"); + + rc = MUR_PLUS2_ALIGNED(al); printf("al plus2 (n): %s\n", yn(rc)); + rc = MUR_PLUS2_ALIGNED(u1); printf("u1 plus2 (n): %s\n", yn(rc)); + rc = MUR_PLUS2_ALIGNED(u2); printf("u2 plus2 (y): %s\n", yn(rc)); + rc = MUR_PLUS2_ALIGNED(u3); printf("u3 plus2 (n): %s\n", yn(rc)); + printf("\n"); + + rc = MUR_PLUS3_ALIGNED(al); printf("al plus3 (n): %s\n", yn(rc)); + rc = MUR_PLUS3_ALIGNED(u1); printf("u1 plus3 (n): %s\n", yn(rc)); + rc = MUR_PLUS3_ALIGNED(u2); printf("u2 plus3 (n): %s\n", yn(rc)); + rc = MUR_PLUS3_ALIGNED(u3); printf("u3 plus3 (y): %s\n", yn(rc)); + printf("\n"); + + /* ---------------------------------------- */ + /* test careful reassembly of an unaligned integer */ +#if 0 /* commented out since result is endian dependent */ + rc = MUR_GETBLOCK(al,0); printf("%x\n", rc); + rc = MUR_GETBLOCK(u1,0); printf("%x\n", rc); + rc = MUR_GETBLOCK(u2,0); printf("%x\n", rc); + rc = MUR_GETBLOCK(u3,0); printf("%x\n", rc); +#endif + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.ans new file mode 100644 index 0000000000..c570c50e2e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.ans @@ -0,0 +1,7 @@ +LL macros +a b c +d e f +a b c d e f +d e f +d e f +a b diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.c new file mode 100644 index 0000000000..488f20b5dc --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test63.c @@ -0,0 +1,47 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *headA = NULL, *headB = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test LL macros */ + printf("LL macros\n"); + LL_APPEND(headA,&els[0]); + LL_APPEND(headA,&els[1]); + LL_APPEND(headA,&els[2]); + LL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + LL_APPEND(headB,&els[3]); + LL_APPEND(headB,&els[4]); + LL_APPEND(headB,&els[5]); + LL_FOREACH(headB,e) printf("%c ", e->id); printf("\n"); + + LL_CONCAT(headA,headB); + LL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + /* other variations */ + headA = NULL; + LL_CONCAT(headA,headB); + LL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + headB = NULL; + LL_CONCAT(headA,headB); + LL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + headA=NULL; + headB=NULL; + LL_APPEND(headA,&els[0]); + LL_APPEND(headB,&els[1]); + LL_CONCAT(headA,headB); + LL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.ans new file mode 100644 index 0000000000..1640241aa7 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.ans @@ -0,0 +1,7 @@ +DL macros +a b c +d e f +a b c d e f +d e f +d e f +a b diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.c new file mode 100644 index 0000000000..9d49e2de59 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test64.c @@ -0,0 +1,47 @@ +#include +#include "utlist.h" + +typedef struct el { + int id; + struct el *next, *prev; +} el; + +el *headA = NULL, *headB = NULL; + +int main(int argc, char *argv[]) { + int i; + el els[10], *e; + for(i=0;i<10;i++) els[i].id='a'+i; + + /* test DL macros */ + printf("DL macros\n"); + DL_APPEND(headA,&els[0]); + DL_APPEND(headA,&els[1]); + DL_APPEND(headA,&els[2]); + DL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + DL_APPEND(headB,&els[3]); + DL_APPEND(headB,&els[4]); + DL_APPEND(headB,&els[5]); + DL_FOREACH(headB,e) printf("%c ", e->id); printf("\n"); + + DL_CONCAT(headA,headB); + DL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + /* other variations */ + headA = NULL; + DL_CONCAT(headA,headB); + DL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + headB = NULL; + DL_CONCAT(headA,headB); + DL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + headA=NULL; + headB=NULL; + DL_APPEND(headA,&els[0]); + DL_APPEND(headB,&els[1]); + DL_CONCAT(headA,headB); + DL_FOREACH(headA,e) printf("%c ", e->id); printf("\n"); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.ans new file mode 100644 index 0000000000..5312b22a2c --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.ans @@ -0,0 +1,4 @@ +LRU deleting JOHN + 0 +LRU deleting WILLIAM + 1 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.c new file mode 100644 index 0000000000..3bf3d4cb4f --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.c @@ -0,0 +1,72 @@ +#include +#include +#include "uthash.h" + +// this is an example of how to do a LRU cache in C using uthash +// http://uthash.sourceforge.net/ +// by Jehiah Czebotar 2011 - jehiah@gmail.com +// this code is in the public domain http://unlicense.org/ + +#define MAX_CACHE_SIZE 50 /* a real value would be much larger */ + +struct CacheEntry { + char *key; + char *value; + UT_hash_handle hh; +}; +struct CacheEntry *cache = NULL; + +char * /*value*/ find_in_cache(char *key) +{ + struct CacheEntry *entry; + HASH_FIND_STR(cache, key, entry); + if (entry) { + // remove it (so the subsequent add will throw it on the front of the list) + HASH_DELETE(hh, cache, entry); + HASH_ADD_KEYPTR(hh, cache, entry->key, strlen(entry->key), entry); + return entry->value; + } + return NULL; +} + +void add_to_cache(char *key, char *value) +{ + struct CacheEntry *entry, *tmp_entry; + entry = malloc(sizeof(struct CacheEntry)); + entry->key = strdup(key); + entry->value = strdup(value); + HASH_ADD_KEYPTR(hh, cache, entry->key, strlen(entry->key), entry); + + // prune the cache to MAX_CACHE_SIZE + if (HASH_COUNT(cache) >= MAX_CACHE_SIZE) { + HASH_ITER(hh, cache, entry, tmp_entry) { + // prune the first entry (loop is based on insertion order so this deletes the oldest item) + printf("LRU deleting %s %s\n", entry->key, entry->value); + HASH_DELETE(hh, cache, entry); + free(entry->key); + free(entry->value); + free(entry); + break; + } + } +} + +/* main added by Troy D. Hanson */ +int main(int argc, char *argv[]) { + char linebuf[100], nbuf[10]; + FILE *file; + int i=0; + + if ( (file = fopen( "test65.dat", "r" )) == NULL ) { + perror("can't open: "); + exit(-1); + } + + while (fgets(linebuf,sizeof(linebuf),file) != NULL) { + snprintf(nbuf,sizeof(nbuf),"%u",i++); + add_to_cache(linebuf, nbuf); + } + + fclose(file); + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.dat b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.dat new file mode 100644 index 0000000000..bb6051b750 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test65.dat @@ -0,0 +1,51 @@ +JOHN +WILLIAM +WALTER +DOUGLAS +GERALD +FREDERICK +WARREN +SHANE +LESTER +RON +HARVEY +ADRIAN +CODY +NELSON +CLIFTON +WILLARD +DOUG +ORLANDO +REX +OMAR +DAMON +LOWELL +IRVING +CARROLL +LAURENCE +ROLANDO +CARY +XAVIER +ISAIAH +GUS +JARVIS +WINFRED +RAYMUNDO +LINCOLN +CORNELL +NIGEL +NORMAND +FRITZ +DONN +TRINIDAD +ODIS +DANNIE +DARIO +KENTON +CHONG +NEVILLE +TONEY +WARNER +WES +COLTON +ARNOLDO diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.ans new file mode 100644 index 0000000000..727f397ebd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.ans @@ -0,0 +1,20 @@ +added bob (id 0) +added jack (id 1) +added gary (id 2) +added ty (id 3) +added bo (id 4) +added phil (id 5) +added art (id 6) +added gil (id 7) +added buck (id 8) +added ted (id 9) +found bob (id 0) +found jack (id 1) +found gary (id 2) +found ty (id 3) +found bo (id 4) +found phil (id 5) +found art (id 6) +found gil (id 7) +found buck (id 8) +found ted (id 9) diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.c new file mode 100644 index 0000000000..d50deb5aae --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test66.c @@ -0,0 +1,37 @@ +#include "uthash.h" +#include +#include /* malloc */ + +typedef struct person_t { + char first_name[10]; + int id; + UT_hash_handle hh; +} person_t; + +int main(int argc, char*argv[]) { + person_t *people=NULL, *person; + const char **name; + const char * names[] = { "bob", "jack", "gary", "ty", "bo", "phil", "art", + "gil", "buck", "ted", NULL }; + int id=0; + + for(name=names; *name; name++) { + if ( (person = (person_t*)malloc(sizeof(person_t))) == NULL) exit(-1); + strncpy(person->first_name, *name,10); + person->id = id++; + HASH_ADD_STR(people,first_name,person); + printf("added %s (id %d)\n", person->first_name, person->id); + } + + person=NULL; + person_t **p=&person; + + for(name=names; *name; name++) { + HASH_FIND_STR(people,*name,*p); + if (person) + printf("found %s (id %d)\n", person->first_name, person->id); + else + printf("failed to find %s\n", *name); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.ans new file mode 100644 index 0000000000..cd707691da --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.ans @@ -0,0 +1,20 @@ +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 +9 +8 +7 +6 +5 +4 +3 +2 +1 +0 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.c new file mode 100644 index 0000000000..00dc01a834 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test67.c @@ -0,0 +1,27 @@ +#include +#include "utarray.h" + +int main() { + UT_array *nums; + int i, *p; + + utarray_new(nums,&ut_int_icd); + for(i=0; i < 10; i++) utarray_push_back(nums,&i); + + for(p=(int*)utarray_back(nums); + p!=NULL; + p=(int*)utarray_prev(nums,p)) { + printf("%d\n",*p); + } + + /* the other form of iteration starting from NULL (back) */ + p=NULL; + while ( (p=(int*)utarray_prev(nums,p))) { + printf("%d\n",*p); + } + + + utarray_free(nums); + + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test7.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test7.ans new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test7.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test7.c new file mode 100644 index 0000000000..3f76c8d5cd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test7.c @@ -0,0 +1,38 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<1000;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* delete each ID */ + for(i=0;i<1000;i++) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + HASH_DEL(users,tmp); + free(tmp); + } else printf("user id %d not found\n", i); + } + + /* show the hash */ + for(user=users; user != NULL; user=(example_user_t*)(user->hh.next)) { + printf("user %d, cookie %d\n", user->id, user->cookie); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.ans new file mode 100644 index 0000000000..9d28857c4e --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.ans @@ -0,0 +1,15 @@ +num_items in hash: 1 +num_items in hash: 2 +num_items in hash: 3 +num_items in hash: 4 +num_items in hash: 5 +num_items in hash: 6 +num_items in hash: 7 +num_items in hash: 8 +num_items in hash: 9 +num_items in hash: 10 +deleted; num_items in hash: 9 +deleted; num_items in hash: 8 +deleted; num_items in hash: 7 +deleted; num_items in hash: 6 +deleted; num_items in hash: 5 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.c new file mode 100644 index 0000000000..f7c7c8dec2 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test8.c @@ -0,0 +1,34 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<10;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + printf("num_items in hash: %d\n", user->hh.tbl->num_items); + } + + /* delete each even ID */ + for(i=0;i<10;i+=2) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + HASH_DEL(users,tmp); + free(tmp); + printf("deleted; num_items in hash: %d\n", user->hh.tbl->num_items); + } else printf("user id %d not found\n", i); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.ans b/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.ans new file mode 100644 index 0000000000..13a9402eda --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.ans @@ -0,0 +1,500 @@ +user 0, cookie 0 +user 2, cookie 4 +user 4, cookie 16 +user 6, cookie 36 +user 8, cookie 64 +user 10, cookie 100 +user 12, cookie 144 +user 14, cookie 196 +user 16, cookie 256 +user 18, cookie 324 +user 20, cookie 400 +user 22, cookie 484 +user 24, cookie 576 +user 26, cookie 676 +user 28, cookie 784 +user 30, cookie 900 +user 32, cookie 1024 +user 34, cookie 1156 +user 36, cookie 1296 +user 38, cookie 1444 +user 40, cookie 1600 +user 42, cookie 1764 +user 44, cookie 1936 +user 46, cookie 2116 +user 48, cookie 2304 +user 50, cookie 2500 +user 52, cookie 2704 +user 54, cookie 2916 +user 56, cookie 3136 +user 58, cookie 3364 +user 60, cookie 3600 +user 62, cookie 3844 +user 64, cookie 4096 +user 66, cookie 4356 +user 68, cookie 4624 +user 70, cookie 4900 +user 72, cookie 5184 +user 74, cookie 5476 +user 76, cookie 5776 +user 78, cookie 6084 +user 80, cookie 6400 +user 82, cookie 6724 +user 84, cookie 7056 +user 86, cookie 7396 +user 88, cookie 7744 +user 90, cookie 8100 +user 92, cookie 8464 +user 94, cookie 8836 +user 96, cookie 9216 +user 98, cookie 9604 +user 100, cookie 10000 +user 102, cookie 10404 +user 104, cookie 10816 +user 106, cookie 11236 +user 108, cookie 11664 +user 110, cookie 12100 +user 112, cookie 12544 +user 114, cookie 12996 +user 116, cookie 13456 +user 118, cookie 13924 +user 120, cookie 14400 +user 122, cookie 14884 +user 124, cookie 15376 +user 126, cookie 15876 +user 128, cookie 16384 +user 130, cookie 16900 +user 132, cookie 17424 +user 134, cookie 17956 +user 136, cookie 18496 +user 138, cookie 19044 +user 140, cookie 19600 +user 142, cookie 20164 +user 144, cookie 20736 +user 146, cookie 21316 +user 148, cookie 21904 +user 150, cookie 22500 +user 152, cookie 23104 +user 154, cookie 23716 +user 156, cookie 24336 +user 158, cookie 24964 +user 160, cookie 25600 +user 162, cookie 26244 +user 164, cookie 26896 +user 166, cookie 27556 +user 168, cookie 28224 +user 170, cookie 28900 +user 172, cookie 29584 +user 174, cookie 30276 +user 176, cookie 30976 +user 178, cookie 31684 +user 180, cookie 32400 +user 182, cookie 33124 +user 184, cookie 33856 +user 186, cookie 34596 +user 188, cookie 35344 +user 190, cookie 36100 +user 192, cookie 36864 +user 194, cookie 37636 +user 196, cookie 38416 +user 198, cookie 39204 +user 200, cookie 40000 +user 202, cookie 40804 +user 204, cookie 41616 +user 206, cookie 42436 +user 208, cookie 43264 +user 210, cookie 44100 +user 212, cookie 44944 +user 214, cookie 45796 +user 216, cookie 46656 +user 218, cookie 47524 +user 220, cookie 48400 +user 222, cookie 49284 +user 224, cookie 50176 +user 226, cookie 51076 +user 228, cookie 51984 +user 230, cookie 52900 +user 232, cookie 53824 +user 234, cookie 54756 +user 236, cookie 55696 +user 238, cookie 56644 +user 240, cookie 57600 +user 242, cookie 58564 +user 244, cookie 59536 +user 246, cookie 60516 +user 248, cookie 61504 +user 250, cookie 62500 +user 252, cookie 63504 +user 254, cookie 64516 +user 256, cookie 65536 +user 258, cookie 66564 +user 260, cookie 67600 +user 262, cookie 68644 +user 264, cookie 69696 +user 266, cookie 70756 +user 268, cookie 71824 +user 270, cookie 72900 +user 272, cookie 73984 +user 274, cookie 75076 +user 276, cookie 76176 +user 278, cookie 77284 +user 280, cookie 78400 +user 282, cookie 79524 +user 284, cookie 80656 +user 286, cookie 81796 +user 288, cookie 82944 +user 290, cookie 84100 +user 292, cookie 85264 +user 294, cookie 86436 +user 296, cookie 87616 +user 298, cookie 88804 +user 300, cookie 90000 +user 302, cookie 91204 +user 304, cookie 92416 +user 306, cookie 93636 +user 308, cookie 94864 +user 310, cookie 96100 +user 312, cookie 97344 +user 314, cookie 98596 +user 316, cookie 99856 +user 318, cookie 101124 +user 320, cookie 102400 +user 322, cookie 103684 +user 324, cookie 104976 +user 326, cookie 106276 +user 328, cookie 107584 +user 330, cookie 108900 +user 332, cookie 110224 +user 334, cookie 111556 +user 336, cookie 112896 +user 338, cookie 114244 +user 340, cookie 115600 +user 342, cookie 116964 +user 344, cookie 118336 +user 346, cookie 119716 +user 348, cookie 121104 +user 350, cookie 122500 +user 352, cookie 123904 +user 354, cookie 125316 +user 356, cookie 126736 +user 358, cookie 128164 +user 360, cookie 129600 +user 362, cookie 131044 +user 364, cookie 132496 +user 366, cookie 133956 +user 368, cookie 135424 +user 370, cookie 136900 +user 372, cookie 138384 +user 374, cookie 139876 +user 376, cookie 141376 +user 378, cookie 142884 +user 380, cookie 144400 +user 382, cookie 145924 +user 384, cookie 147456 +user 386, cookie 148996 +user 388, cookie 150544 +user 390, cookie 152100 +user 392, cookie 153664 +user 394, cookie 155236 +user 396, cookie 156816 +user 398, cookie 158404 +user 400, cookie 160000 +user 402, cookie 161604 +user 404, cookie 163216 +user 406, cookie 164836 +user 408, cookie 166464 +user 410, cookie 168100 +user 412, cookie 169744 +user 414, cookie 171396 +user 416, cookie 173056 +user 418, cookie 174724 +user 420, cookie 176400 +user 422, cookie 178084 +user 424, cookie 179776 +user 426, cookie 181476 +user 428, cookie 183184 +user 430, cookie 184900 +user 432, cookie 186624 +user 434, cookie 188356 +user 436, cookie 190096 +user 438, cookie 191844 +user 440, cookie 193600 +user 442, cookie 195364 +user 444, cookie 197136 +user 446, cookie 198916 +user 448, cookie 200704 +user 450, cookie 202500 +user 452, cookie 204304 +user 454, cookie 206116 +user 456, cookie 207936 +user 458, cookie 209764 +user 460, cookie 211600 +user 462, cookie 213444 +user 464, cookie 215296 +user 466, cookie 217156 +user 468, cookie 219024 +user 470, cookie 220900 +user 472, cookie 222784 +user 474, cookie 224676 +user 476, cookie 226576 +user 478, cookie 228484 +user 480, cookie 230400 +user 482, cookie 232324 +user 484, cookie 234256 +user 486, cookie 236196 +user 488, cookie 238144 +user 490, cookie 240100 +user 492, cookie 242064 +user 494, cookie 244036 +user 496, cookie 246016 +user 498, cookie 248004 +user 500, cookie 250000 +user 502, cookie 252004 +user 504, cookie 254016 +user 506, cookie 256036 +user 508, cookie 258064 +user 510, cookie 260100 +user 512, cookie 262144 +user 514, cookie 264196 +user 516, cookie 266256 +user 518, cookie 268324 +user 520, cookie 270400 +user 522, cookie 272484 +user 524, cookie 274576 +user 526, cookie 276676 +user 528, cookie 278784 +user 530, cookie 280900 +user 532, cookie 283024 +user 534, cookie 285156 +user 536, cookie 287296 +user 538, cookie 289444 +user 540, cookie 291600 +user 542, cookie 293764 +user 544, cookie 295936 +user 546, cookie 298116 +user 548, cookie 300304 +user 550, cookie 302500 +user 552, cookie 304704 +user 554, cookie 306916 +user 556, cookie 309136 +user 558, cookie 311364 +user 560, cookie 313600 +user 562, cookie 315844 +user 564, cookie 318096 +user 566, cookie 320356 +user 568, cookie 322624 +user 570, cookie 324900 +user 572, cookie 327184 +user 574, cookie 329476 +user 576, cookie 331776 +user 578, cookie 334084 +user 580, cookie 336400 +user 582, cookie 338724 +user 584, cookie 341056 +user 586, cookie 343396 +user 588, cookie 345744 +user 590, cookie 348100 +user 592, cookie 350464 +user 594, cookie 352836 +user 596, cookie 355216 +user 598, cookie 357604 +user 600, cookie 360000 +user 602, cookie 362404 +user 604, cookie 364816 +user 606, cookie 367236 +user 608, cookie 369664 +user 610, cookie 372100 +user 612, cookie 374544 +user 614, cookie 376996 +user 616, cookie 379456 +user 618, cookie 381924 +user 620, cookie 384400 +user 622, cookie 386884 +user 624, cookie 389376 +user 626, cookie 391876 +user 628, cookie 394384 +user 630, cookie 396900 +user 632, cookie 399424 +user 634, cookie 401956 +user 636, cookie 404496 +user 638, cookie 407044 +user 640, cookie 409600 +user 642, cookie 412164 +user 644, cookie 414736 +user 646, cookie 417316 +user 648, cookie 419904 +user 650, cookie 422500 +user 652, cookie 425104 +user 654, cookie 427716 +user 656, cookie 430336 +user 658, cookie 432964 +user 660, cookie 435600 +user 662, cookie 438244 +user 664, cookie 440896 +user 666, cookie 443556 +user 668, cookie 446224 +user 670, cookie 448900 +user 672, cookie 451584 +user 674, cookie 454276 +user 676, cookie 456976 +user 678, cookie 459684 +user 680, cookie 462400 +user 682, cookie 465124 +user 684, cookie 467856 +user 686, cookie 470596 +user 688, cookie 473344 +user 690, cookie 476100 +user 692, cookie 478864 +user 694, cookie 481636 +user 696, cookie 484416 +user 698, cookie 487204 +user 700, cookie 490000 +user 702, cookie 492804 +user 704, cookie 495616 +user 706, cookie 498436 +user 708, cookie 501264 +user 710, cookie 504100 +user 712, cookie 506944 +user 714, cookie 509796 +user 716, cookie 512656 +user 718, cookie 515524 +user 720, cookie 518400 +user 722, cookie 521284 +user 724, cookie 524176 +user 726, cookie 527076 +user 728, cookie 529984 +user 730, cookie 532900 +user 732, cookie 535824 +user 734, cookie 538756 +user 736, cookie 541696 +user 738, cookie 544644 +user 740, cookie 547600 +user 742, cookie 550564 +user 744, cookie 553536 +user 746, cookie 556516 +user 748, cookie 559504 +user 750, cookie 562500 +user 752, cookie 565504 +user 754, cookie 568516 +user 756, cookie 571536 +user 758, cookie 574564 +user 760, cookie 577600 +user 762, cookie 580644 +user 764, cookie 583696 +user 766, cookie 586756 +user 768, cookie 589824 +user 770, cookie 592900 +user 772, cookie 595984 +user 774, cookie 599076 +user 776, cookie 602176 +user 778, cookie 605284 +user 780, cookie 608400 +user 782, cookie 611524 +user 784, cookie 614656 +user 786, cookie 617796 +user 788, cookie 620944 +user 790, cookie 624100 +user 792, cookie 627264 +user 794, cookie 630436 +user 796, cookie 633616 +user 798, cookie 636804 +user 800, cookie 640000 +user 802, cookie 643204 +user 804, cookie 646416 +user 806, cookie 649636 +user 808, cookie 652864 +user 810, cookie 656100 +user 812, cookie 659344 +user 814, cookie 662596 +user 816, cookie 665856 +user 818, cookie 669124 +user 820, cookie 672400 +user 822, cookie 675684 +user 824, cookie 678976 +user 826, cookie 682276 +user 828, cookie 685584 +user 830, cookie 688900 +user 832, cookie 692224 +user 834, cookie 695556 +user 836, cookie 698896 +user 838, cookie 702244 +user 840, cookie 705600 +user 842, cookie 708964 +user 844, cookie 712336 +user 846, cookie 715716 +user 848, cookie 719104 +user 850, cookie 722500 +user 852, cookie 725904 +user 854, cookie 729316 +user 856, cookie 732736 +user 858, cookie 736164 +user 860, cookie 739600 +user 862, cookie 743044 +user 864, cookie 746496 +user 866, cookie 749956 +user 868, cookie 753424 +user 870, cookie 756900 +user 872, cookie 760384 +user 874, cookie 763876 +user 876, cookie 767376 +user 878, cookie 770884 +user 880, cookie 774400 +user 882, cookie 777924 +user 884, cookie 781456 +user 886, cookie 784996 +user 888, cookie 788544 +user 890, cookie 792100 +user 892, cookie 795664 +user 894, cookie 799236 +user 896, cookie 802816 +user 898, cookie 806404 +user 900, cookie 810000 +user 902, cookie 813604 +user 904, cookie 817216 +user 906, cookie 820836 +user 908, cookie 824464 +user 910, cookie 828100 +user 912, cookie 831744 +user 914, cookie 835396 +user 916, cookie 839056 +user 918, cookie 842724 +user 920, cookie 846400 +user 922, cookie 850084 +user 924, cookie 853776 +user 926, cookie 857476 +user 928, cookie 861184 +user 930, cookie 864900 +user 932, cookie 868624 +user 934, cookie 872356 +user 936, cookie 876096 +user 938, cookie 879844 +user 940, cookie 883600 +user 942, cookie 887364 +user 944, cookie 891136 +user 946, cookie 894916 +user 948, cookie 898704 +user 950, cookie 902500 +user 952, cookie 906304 +user 954, cookie 910116 +user 956, cookie 913936 +user 958, cookie 917764 +user 960, cookie 921600 +user 962, cookie 925444 +user 964, cookie 929296 +user 966, cookie 933156 +user 968, cookie 937024 +user 970, cookie 940900 +user 972, cookie 944784 +user 974, cookie 948676 +user 976, cookie 952576 +user 978, cookie 956484 +user 980, cookie 960400 +user 982, cookie 964324 +user 984, cookie 968256 +user 986, cookie 972196 +user 988, cookie 976144 +user 990, cookie 980100 +user 992, cookie 984064 +user 994, cookie 988036 +user 996, cookie 992016 +user 998, cookie 996004 diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.c new file mode 100644 index 0000000000..a8a16dd35d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/test9.c @@ -0,0 +1,31 @@ +#include "uthash.h" +#include /* malloc */ +#include /* printf */ + +typedef struct example_user_t { + int id; + int cookie; + UT_hash_handle hh; +} example_user_t; + +int main(int argc,char *argv[]) { + int i; + example_user_t *user, *tmp, *users=NULL; + + /* create elements */ + for(i=0;i<1000;i++) { + if ( (user = (example_user_t*)malloc(sizeof(example_user_t))) == NULL) exit(-1); + user->id = i; + user->cookie = i*i; + HASH_ADD_INT(users,id,user); + } + + /* delete each ID */ + for(i=0;i<1000;i+=2) { + HASH_FIND_INT(users,&i,tmp); + if (tmp) { + printf("user %d, cookie %d\n", tmp->id, tmp->cookie); + } else printf("user id %d not found\n", i); + } + return 0; +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/Makefile b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/Makefile new file mode 100644 index 0000000000..4140fa7dc0 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/Makefile @@ -0,0 +1,31 @@ +HASHDIR = ../../src +PROGS = test1 test2 + +# Thread support requires compiler-specific options +# ---------------------------------------------------------------------------- +# GNU +CFLAGS = -I$(HASHDIR) -g -pthread +# Solaris (Studio 11) +#CFLAGS = -I$(HASHDIR) -g -mt +# ---------------------------------------------------------------------------- + +ifeq ($(HASH_DEBUG),1) +CFLAGS += -DHASH_DEBUG=1 +endif + +all: $(PROGS) run_tests + +$(PROGS) : $(HASHDIR)/uthash.h + $(CC) $(CFLAGS) -o $@ $(@).c + +debug: + $(MAKE) all HASH_DEBUG=1 + +run_tests: $(PROGS) + perl ../do_tests + +.PHONY: clean + +clean: + rm -f $(PROGS) test*.out + rm -rf test*.dSYM diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/README b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/README new file mode 100644 index 0000000000..e49a62eedd --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/README @@ -0,0 +1,2 @@ +test1: exercise a two-reader, one-writer, rwlock-protected hash. +test2: a template for a nthread, nloop kind of program diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/do_tests b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/do_tests new file mode 100755 index 0000000000..e6809a157c --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/do_tests @@ -0,0 +1,22 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my @tests; +for (glob "test*[0-9]") { + push @tests, $_ if -e "$_.ans"; +} + +my $num_failed=0; + +for my $test (@tests) { + `./$test > $test.out 2> $test.err`; + `diff $test.out $test.ans`; + print "$test failed\n" if $?; + $num_failed++ if $?; + unlink "$test.err" if -z "$test.err"; +} + +print scalar @tests . " tests conducted, $num_failed failed.\n"; +exit $num_failed; diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test1.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test1.c new file mode 100644 index 0000000000..104508677d --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test1.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uthash.h" + +#undef uthash_noexpand_fyi +#define uthash_noexpand_fyi fprintf(stderr,"warning: bucket expansion inhibited\n"); + +#define LOOPS 100000 + +typedef struct { + int i; + UT_hash_handle hh; +} elt; + +elt *elts=NULL; /* this is our hash table which two threads will use */ +pthread_rwlock_t lock; + +void *thread_routine_r( void *arg ) { + int i; + long num_found=0; + elt *e; + + for(i=0;ii = i; + HASH_ADD_INT(elts, i, e); + } + pthread_rwlock_unlock(&lock); + } + return (void*)num_deld; +} + +int main() { + unsigned i; + long num_added=0; + int status; + pthread_t thread_r1,thread_r2,thread_w1,thread_w2; + void *thread_result; + elt tmp, *e; + + if (pthread_rwlock_init(&lock,NULL) != 0) { + fprintf(stderr,"lock init failed\n"); + exit(-1); + } + + if ( status = pthread_create( &thread_r1, NULL, thread_routine_r, NULL )) { + printf("failure: status %d\n", status); + exit(-1); + } + if ( status = pthread_create( &thread_r2, NULL, thread_routine_r, NULL )) { + printf("failure: status %d\n", status); + exit(-1); + } + if ( status = pthread_create( &thread_w1, NULL, thread_routine_w, NULL )) { + printf("failure: status %d\n", status); + exit(-1); + } + if ( status = pthread_create( &thread_w2, NULL, thread_routine_w, NULL )) { + printf("failure: status %d\n", status); + exit(-1); + } + + status = pthread_join( thread_r1, &thread_result ); + printf("thread result: %d %ld\n", status, (long)thread_result); + + status = pthread_join( thread_r2, &thread_result ); + printf("thread result: %d %ld\n", status, (long)thread_result); + + status = pthread_join( thread_w1, &thread_result ); + printf("thread result: %d %ld\n", status, (long)thread_result); + + status = pthread_join( thread_w2, &thread_result ); + printf("thread result: %d %ld\n", status, (long)thread_result); + + i = HASH_COUNT(elts); + printf("final count of items in hash: %u\n", i); +} diff --git a/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test2.c b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test2.c new file mode 100644 index 0000000000..20d984f3a1 --- /dev/null +++ b/ami_test_client/libc-jss/uthash-1.9.6/tests/threads/test2.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uthash.h" + +#undef uthash_noexpand_fyi +#define uthash_noexpand_fyi fprintf(stderr,"warning: bucket expansion inhibited\n"); + +#define LOOPS 100000 +#define NTHREADS 2 + +typedef struct { + int i; + int v; + UT_hash_handle hh; +} elt; + +elt *elts=NULL; /* this is our hash table which two threads will use */ +pthread_rwlock_t lock; + +void *thread_routine( void *arg ) { + int keepgoing=0; + /* TODO write me */ + return NULL; +} + +int main() { + unsigned i; + long num_added=0; + int status; + pthread_t thread[NTHREADS]; + void *thread_result; + elt tmp, *e; + + if (pthread_rwlock_init(&lock,NULL) != 0) { + fprintf(stderr,"lock init failed\n"); + exit(-1); + } + + /* populate it to start */ + for(i=0; ii = i; + e->v = 0; + HASH_ADD_INT(elts, i, e); + } + + for(i=0; i /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#else +#include /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.6 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = (unsigned)keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((char*)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + char *_hj_key=(char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + char *_sfh_key=(char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + uint32_t _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + const uint8_t *_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + uint32_t _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + _hs_tail->next = NULL; \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/ami_test_client/libc-jss/utlist.h b/ami_test_client/libc-jss/utlist.h new file mode 100644 index 0000000000..1578acad2f --- /dev/null +++ b/ami_test_client/libc-jss/utlist.h @@ -0,0 +1,522 @@ +/* +Copyright (c) 2007-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 1.9.6 + +#include + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ code), this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define LDECLTYPE(x) char* +#endif +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define _NEXT(elt,list) ((char*)((list)->next)) +#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +#define _PREV(elt,list) ((char*)((list)->prev)) +#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define _SV(elt,list) +#define _NEXT(elt,list) ((elt)->next) +#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) +#define _PREV(elt,list) ((elt)->prev) +#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) +#define _RS(list) +#define _CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define DL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev, _ls_tail); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +#define CDL_SORT(list, cmp) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + LDECLTYPE(list) _tmp2; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + _CASTASGN(_ls_p,list); \ + _CASTASGN(_ls_oldhead,list); \ + list = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + _SV(_ls_q,list); \ + if (_NEXT(_ls_q,list) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = _NEXT(_ls_q,list); \ + } \ + _RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ + } else { \ + _CASTASGN(list,_ls_e); \ + } \ + _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + _CASTASGN(list->prev,_ls_tail); \ + _CASTASGN(_tmp2,list); \ + _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } else _tmp=NULL; /* quiet gcc unused variable warning */ \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + head = add; \ +} while (0) + +#define LL_CONCAT(head1,head2) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = head1; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = head; \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_DELETE(head,del) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = head; \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = ((del)->next); \ + } \ + } \ +} while (0) + +/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ +#define LL_APPEND_VS2008(head,add) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#define LL_DELETE_VS2008(head,del) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + head = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + { \ + char **_head_alias = (char**)&(head); \ + *_head_alias = _tmp; \ + } \ + } \ +} while (0) +#ifdef NO_DECLTYPE +#undef LL_APPEND +#define LL_APPEND LL_APPEND_VS2008 +#undef LL_DELETE +#define LL_DELETE LL_DELETE_VS2008 +#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ +#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ +#endif +/* end VS2008 replacements */ + +#define LL_FOREACH(head,el) \ + for(el=head;el;el=(el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + LL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define LL_SEARCH(head,out,elt,cmp) \ +do { \ + LL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ +do { \ + (add)->next = head; \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + _tmp = (head2)->prev; \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + (head1)->prev = _tmp; \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ +do { \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + + +#define DL_FOREACH(head,el) \ + for(el=head;el;el=(el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_PREPEND(head,add) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ +(head)=(add); \ +} while (0) + +#define CDL_DELETE(head,del) \ +do { \ + if ( ((head)==(del)) && ((head)->next == (head))) { \ + (head) = 0L; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ + (el) && ((tmp2)=(el)->next, 1); \ + ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((out)->field == (val)) break; \ + } \ +} while(0) + +#define CDL_SEARCH(head,out,elt,cmp) \ +do { \ + CDL_FOREACH(head,out) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while(0) + +#endif /* UTLIST_H */ + diff --git a/ami_test_client/libc-jss/utstring.h b/ami_test_client/libc-jss/utstring.h new file mode 100644 index 0000000000..a181ad7785 --- /dev/null +++ b/ami_test_client/libc-jss/utstring.h @@ -0,0 +1,148 @@ +/* +Copyright (c) 2008-2012, Troy D. Hanson http://uthash.sourceforge.net +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. + +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. +*/ + +/* a dynamic string implementation using macros + * see http://uthash.sourceforge.net/utstring + */ +#ifndef UTSTRING_H +#define UTSTRING_H + +#define UTSTRING_VERSION 1.9.6 + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((__unused__)) +#else +#define _UNUSED_ +#endif + +#include +#include +#include +#define oom() exit(-1) + +typedef struct { + char *d; + size_t n; /* allocd size */ + size_t i; /* index of first unused byte */ +} UT_string; + +#define utstring_reserve(s,amt) \ +do { \ + if (((s)->n - (s)->i) < (size_t)(amt)) { \ + (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ + if ((s)->d == NULL) oom(); \ + (s)->n += amt; \ + } \ +} while(0) + +#define utstring_init(s) \ +do { \ + (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ + utstring_reserve(s,100); \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_done(s) \ +do { \ + if ((s)->d != NULL) free((s)->d); \ + (s)->n = 0; \ +} while(0) + +#define utstring_free(s) \ +do { \ + utstring_done(s); \ + free(s); \ +} while(0) + +#define utstring_new(s) \ +do { \ + s = (UT_string*)calloc(sizeof(UT_string),1); \ + if (!s) oom(); \ + utstring_init(s); \ +} while(0) + +#define utstring_renew(s) \ +do { \ + if (s) { \ + utstring_clear(s); \ + } else { \ + utstring_new(s); \ + } \ +} while(0) + +#define utstring_clear(s) \ +do { \ + (s)->i = 0; \ + (s)->d[0] = '\0'; \ +} while(0) + +#define utstring_bincpy(s,b,l) \ +do { \ + utstring_reserve((s),(l)+1); \ + if (l) memcpy(&(s)->d[(s)->i], b, l); \ + (s)->i += l; \ + (s)->d[(s)->i]='\0'; \ +} while(0) + +#define utstring_concat(dst,src) \ +do { \ + utstring_reserve((dst),((src)->i)+1); \ + if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ + (dst)->i += (src)->i; \ + (dst)->d[(dst)->i]='\0'; \ +} while(0) + +#define utstring_len(s) ((unsigned)((s)->i)) + +#define utstring_body(s) ((s)->d) + +_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { + int n; + va_list cp; + while (1) { +#ifdef _WIN32 + cp = ap; +#else + va_copy(cp, ap); +#endif + n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); + va_end(cp); + + if ((n > -1) && (n < (int)(s->n-s->i))) { + s->i += n; + return; + } + + /* Else try again with more space. */ + if (n > -1) utstring_reserve(s,n+1); /* exact */ + else utstring_reserve(s,(s->n)*2); /* 2x */ + } +} +_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + utstring_printf_va(s,fmt,ap); + va_end(ap); +} + +#endif /* UTSTRING_H */ diff --git a/ami_test_client/milenage.c b/ami_test_client/milenage.c new file mode 100644 index 0000000000..442ec15048 --- /dev/null +++ b/ami_test_client/milenage.c @@ -0,0 +1,429 @@ +/* + * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + * Copyright (c) 2006-2007 + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file implements an example authentication algorithm defined for 3GPP + * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow + * EAP-AKA to be tested properly with real USIM cards. + * + * This implementations assumes that the r1..r5 and c1..c5 constants defined in + * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, + * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to + * be AES (Rijndael). + */ + + +#include "milenage.h" + +#include /* for ERR_print_errors_fp */ +#include /* for NID_sha1, RSA */ +#include /* for EVP_PKEY, EVP_sha1(), ... */ +#include /* for MD5_DIGEST_LENGTH */ +#include /* for SHA_DIGEST_LENGTH */ + +#define AST_CRYPTO_RSA_KEY_BITS 1024 +#define AST_CRYPTO_AES_BLOCKSIZE 128 + +typedef struct ast_aes { + unsigned char raw[AST_CRYPTO_AES_BLOCKSIZE / 8]; +} ast_aes_encrypt_key; + +int ast_aes_set_encrypt_key(const unsigned char *key, ast_aes_encrypt_key *ctx) +{ + if (key == NULL || ctx == NULL) { + return -1; + } + memcpy(ctx->raw, key, AST_CRYPTO_AES_BLOCKSIZE / 8); + return 0; +} + +static int evp_cipher_aes_encrypt(const unsigned char *in, unsigned char *out, unsigned inlen, const ast_aes_encrypt_key *key) +{ + EVP_CIPHER_CTX *ctx = NULL; + int res, outlen, finallen; + unsigned char final[AST_CRYPTO_AES_BLOCKSIZE / 8]; + + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) { + return -1; + } + + do { + if ((res = EVP_CipherInit(ctx, EVP_aes_128_ecb(), key->raw, NULL, 1)) <= 0) { + break; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + if ((res = EVP_CipherUpdate(ctx, out, &outlen, in, inlen)) <= 0) { + break; + } + /* for ECB, this is a no-op */ + if ((res = EVP_CipherFinal(ctx, final, &finallen)) <= 0) { + break; + } + + res = outlen; + } while (0); + + EVP_CIPHER_CTX_free(ctx); + + return res; +} + +int ast_aes_encrypt(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *key) +{ + int res; + + if ((res = evp_cipher_aes_encrypt(in, out, AST_CRYPTO_AES_BLOCKSIZE / 8, key)) <= 0) { + printf("AES encryption failed\n"); + exit (0); + } + return res; +} + +static int aes_128_encrypt_block(const u8 *key, const u8 *plain, u8 *encr) +{ + ast_aes_encrypt_key aes_key; + + ast_aes_set_encrypt_key(key, &aes_key); + if (ast_aes_encrypt(plain, encr, &aes_key) <= 0) { + printf("Failed to ecrypt AES 128."); + return -1; + } + + return 0; +} + +#if 0 +void hexdump(int level, const char *file, int line, const char *func, const char *text, const uint8_t *data, int len) +{ + char s[3 * len + 2], *p; + int f; + + for (p = s, f = 0; f < len; f++, p += 3) { + sprintf(p, "%02hhX ", (unsigned char)data[f]); + } + ast_log(level, file, line, func, "%s: %s\n", text, s); +} +#endif + + +/** + * milenage_f1 - Milenage f1 and f1* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sqn: SQN = 48-bit sequence number + * @amf: AMF = 16-bit authentication management field + * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL + * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL + * Returns: 0 on success, -1 on failure + */ +int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; + + /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ + memcpy(tmp2, sqn, 6); + memcpy(tmp2 + 6, amf, 2); + memcpy(tmp2 + 8, tmp2, 8); + + /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ + + /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ + for (i = 0; i < 16; i++) + tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; + /* XOR with TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp3[i] ^= tmp1[i]; + /* XOR with c1 (= ..00, i.e., NOP) */ + + /* f1 || f1* = E_K(tmp3) XOR OP_c */ + if (aes_128_encrypt_block(k, tmp3, tmp1)) + return -1; + for (i = 0; i < 16; i++) + tmp1[i] ^= opc[i]; + if (mac_a) + memcpy(mac_a, tmp1, 8); /* f1 */ + if (mac_s) + memcpy(mac_s, tmp1 + 8, 8); /* f1* */ + return 0; +} + + +/** + * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL + * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL + * Returns: 0 on success, -1 on failure + */ +int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + if (aes_128_encrypt_block(k, tmp1, tmp2)) + return -1; + + /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ + /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ + /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ + /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ + + /* f2 and f5 */ + /* rotate by r2 (= 0, i.e., NOP) */ + for (i = 0; i < 16; i++) + tmp1[i] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 1; /* XOR c2 (= ..01) */ + /* f5 || f2 = E_K(tmp1) XOR OP_c */ + if (aes_128_encrypt_block(k, tmp1, tmp3)) + return -1; + for (i = 0; i < 16; i++) + tmp3[i] ^= opc[i]; + if (res) + memcpy(res, tmp3 + 8, 8); /* f2 */ + if (ak) + memcpy(ak, tmp3, 6); /* f5 */ + + /* f3 */ + if (ck) { + /* rotate by r3 = 0x20 = 4 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 2; /* XOR c3 (= ..02) */ + if (aes_128_encrypt_block(k, tmp1, ck)) + return -1; + for (i = 0; i < 16; i++) + ck[i] ^= opc[i]; + } + + /* f4 */ + if (ik) { + /* rotate by r4 = 0x40 = 8 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 4; /* XOR c4 (= ..04) */ + if (aes_128_encrypt_block(k, tmp1, ik)) + return -1; + for (i = 0; i < 16; i++) + ik[i] ^= opc[i]; + } + + /* f5* */ + if (akstar) { + /* rotate by r5 = 0x60 = 12 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 8; /* XOR c5 (= ..08) */ + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; + for (i = 0; i < 6; i++) + akstar[i] = tmp1[i] ^ opc[i]; + } + + return 0; +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @amf: AMF = 16-bit authentication management field + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: Buffer for AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Max length for res; set to used length or 0 on failure + */ +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len) +{ + int i; + u8 mac_a[8], ak[6]; + + if (*res_len < 8) { + *res_len = 0; + return; + } + if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || + milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { + *res_len = 0; + return; + } + *res_len = 8; + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + autn[i] = sqn[i] ^ ak[i]; + memcpy(autn + 6, amf, 2); + memcpy(autn + 8, mac_a, 8); +} + + +/** + * milenage_auts - Milenage AUTS validation + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @auts: AUTS = 112-bit authentication token from client + * @sqn: Buffer for SQN = 48-bit sequence number + * Returns: 0 = success (sqn filled), -1 on failure + */ +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn) +{ + u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + u8 ak[6], mac_s[8]; + int i; + + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; + for (i = 0; i < 6; i++) + sqn[i] = auts[i] ^ ak[i]; + if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || + memcmp(mac_s, auts + 6, 8) != 0) + return -1; + return 0; +} + + +/** + * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sres: Buffer for SRES = 32-bit SRES + * @kc: Buffer for Kc = 64-bit Kc + * Returns: 0 on success, -1 on failure + */ +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) +{ + u8 res[8], ck[16], ik[16]; + int i; + + if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) + return -1; + + for (i = 0; i < 8; i++) + kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; + +#ifdef GSM_MILENAGE_ALT_SRES + memcpy(sres, res, 4); +#else /* GSM_MILENAGE_ALT_SRES */ + for (i = 0; i < 4; i++) + sres[i] = res[i] ^ res[i + 4]; +#endif /* GSM_MILENAGE_ALT_SRES */ + return 0; +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Variable that will be set to RES length + * @auts: 112-bit buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 on synchronization failure + */ +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts) +{ + int i; + u8 mac_a[8], ak[6], rx_sqn[6]; + const u8 *amf; + +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: OPC", opc, 16); + hexdump(LOG_DEBUG, "Milenage: K", k, 16); + hexdump(LOG_DEBUG, "Milenage: AUTN", autn, 16); + hexdump(LOG_DEBUG, "Milenage: RAND", _rand, 16); +#endif + + if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) + return -1; + + *res_len = 8; +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: RES", res, *res_len); + hexdump(LOG_DEBUG, "Milenage: CK", ck, 16); + hexdump(LOG_DEBUG, "Milenage: IK", ik, 16); + hexdump(LOG_DEBUG, "Milenage: AK", ak, 6); +#endif + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + rx_sqn[i] = autn[i] ^ ak[i]; +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: RX SQN", rx_sqn, 6); + hexdump(LOG_DEBUG, "Milenage: SQN", sqn, 6); +#endif + + if (memcmp(rx_sqn, sqn, 6) <= 0) { + u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: AK*", ak, 6); +#endif + for (i = 0; i < 6; i++) + auts[i] = sqn[i] ^ ak[i]; + if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) + return -1; +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: AUTS", auts, 14); +#endif + return -2; + } + + amf = autn + 6; +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: AMF", amf, 2); +#endif + if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) + return -1; + +#ifdef hexdump + hexdump(LOG_DEBUG, "Milenage: MAC_A", mac_a, 8); +#endif + + if (memcmp(mac_a, autn + 8, 8) != 0) { +#ifdef hexdump + ast_log(LOG_DEBUG, "Milenage: MAC mismatch"); + hexdump(LOG_DEBUG, "Milenage: Received MAC_A", + autn + 8, 8); +#endif + return -1; + } + + return 0; +} diff --git a/ami_test_client/milenage.h b/ami_test_client/milenage.h new file mode 100644 index 0000000000..238df626f8 --- /dev/null +++ b/ami_test_client/milenage.h @@ -0,0 +1,22 @@ + +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; + +int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s); +int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar); +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len); +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn); +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc); +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts); diff --git a/ami_test_client/originate.c b/ami_test_client/originate.c new file mode 100644 index 0000000000..fda78db1db --- /dev/null +++ b/ami_test_client/originate.c @@ -0,0 +1,337 @@ +/* originate.c + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +// originate.c-hez tartozó con_debug() üzenetek beforduljanak -e +//~ #define CON_DEBUG + +// ami_event_dump() futtatása az originate eseményekre (kommentezd ha nem kell!) +//~ #define AMI_ORIGINATE_DUMP_EVENTS + +// TODO: kigyomlálni a nem kellő include-okat +#include +#include +#include +#include +#include +#include +#include +#include // gettimeofday() + +#include "originate.h" +#include "debug.h" +#include "utlist.h" +#include "misc.h" +#include "logger.h" + +/* Működés: + +Asterisk 1.4 esetén +1. > Action Originate +2. < a visszajövő Response tartalmazza az UniqueID-t +3. < Event Newcallerid tartalmazza a Channel-t +4. < Local channel esetén a Hangup Cause-t a Local-Slave-Channel-ről vesszük + +Asterisk 1.6 és magasabb esetén +1. > Action Originate, benne egy Variable: ami_originate_id = ori->oid +2. < a visszajövő Response nem tartalmaz érdemi információt, nem foglalkozunk vele +3. < Event Varset tartalmazza a fent megadott ami_origate_id-t, Channelt- és UniqueID-t + +A továbbiakban Newstate esemény mutatja a hívás állapotát és a Hangup pedig +a bontás okát, illetve az "Event: OriginateResponse\nResponse: Failure" +jelzi az Originate azonnali kudarcát. + +*/ + +// 6 byte-os random hexa stringet masol az ami->uuid bufferbe +// TODO: egy rendes, unique ID-t visszaado fuggvenyt irni ehelyett a random vacak helyett +static void generate_oid (char *dst, size_t size) { + struct timeval tv; + int num; + char tmp[16]; + + gettimeofday(&tv, NULL); + srand(tv.tv_usec * tv.tv_sec); + num = rand(); + snprintf(tmp, sizeof(tmp), "%x", num); + tmp[6] = '\0'; + strncpy(dst, tmp, size - 1); +} + +static void invoke_callback (ori_t *ori, ami_event_t *event) { + ori->callback(ori, event); +} + +static void got_ami_event (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + +#ifdef AMI_ORIGINATE_DUMP_EVENTS + ami_event_dump(event); +#endif + + ori->state = ORI_UNKNOWN; + + char *state_var_name = (ori->asterisk_version == ASTERISK14) + ? "State" // Asterisk 1.4 + : "ChannelStateDesc"; // Asterisk 1.6 + con_debug("requested variable name in Newstate event is %s", state_var_name); + + if (!strcmp(ami_getvar(event, "Event"), "Newstate")) { + if (!strcmp(ami_getvar(event, state_var_name), "Dialing")) { + ori->state = ORI_DIALING; + } else if (!strcmp(ami_getvar(event, state_var_name), "Ringing")) { + ori->state = ORI_RINGING; + } else if (!strcmp(ami_getvar(event, state_var_name), "Up")) { + ori->state = ORI_ANSWERED; + } else if (!strcmp(ami_getvar(event, state_var_name), "Down")) { + con_debug("ignoring Event=Newstate %s=Down", state_var_name); + return; + } + } else if (!strcmp(ami_getvar(event, "Event"), "Hangup")) { + ori->state = ORI_HANGUP; + // ha még nem tudjuk a hangupcause-t (AST 1.4 esetén event_hangup2 tudja meg előbb) + if (!ori->hangupcause) { + ori->hangupcause = atoi(ami_getvar(event, "Cause")); + strncpy(ori->hangupcausetxt, ami_getvar(event, "Cause-txt"), sizeof(ori->hangupcausetxt) - 1); + } + } + + invoke_callback(ori, event); +} + +/* Asterisk 1.4 esetén innen tudjuk meg a Hangup Cause és Cause-txt +értékeket. Magát a Hangup eseményt nem innen vesszük, mert majd ezután fog +érkezni az igazi Hangup. Abban a Hangupban rossz lesz a Cause. Ezért vesszük +innen a Cause-t:) */ +static void event_hangup2_cb (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + + ori->hangupcause = atoi(ami_getvar(event, "Cause")); + strncpy(ori->hangupcausetxt, ami_getvar(event, "Cause-txt"), sizeof(ori->hangupcausetxt) - 1); + con_debug("got the real hangup cause = %d, waiting for real hangup", ori->hangupcause); +} + +static void got_uniqueid_and_channel (ori_t *ori, char *uniqueid, char *channel) { + ami_t *ami = ori->ami; + if (uniqueid) { + strncpy(ori->uniqueid, uniqueid, sizeof(ori->uniqueid) - 1); + // Hangup figyelés Uniqueid alapján + ori->event_hangup = ami_event_register( + ami, got_ami_event, ori, + "Event: Hangup\nUniqueid: %s", + uniqueid + ); + } + if (channel) { + /* Ha megvan a channel, akkor már nem érdekes a Newcallerid */ + if (ori->event_newcallerid) { + ami_event_unregister(ami, ori->event_newcallerid); + ori->event_newcallerid = NULL; + } + + /* Asterisk 1.4 alatt a Local channel driver nem adja vissza + korrekten a Hangup cause-t, hanem a cause értéke mindig 0 lesz. A + Local egyik oldalán (slave) még ott van a cause kód, de a másik + oldalán (master) már csak egy 0 jelenik meg. Ezen a ponton csak a + master-t ismerjük. Az alábbi kódrészlet megvizsgálja, hogy Asterisk + 1.4-hez csatlakoztunk -e és Local segítségével indítottuk -e a + hívást. + + A Local channel driver két csatornát hoz létre, melyek párban + vannak. Az egyik csatorna (Local-Master-Channel) az, amihez az + Uniqueid is tartozik, a másik csatorna (Local-Slave-Channel) pedig + az, amelyik ténylegesen a fizikai (pl. DAHDI) csatornához + kapcsolódik. A Local-Slave-Channel nevéről az AMI láncban nincs + közvetlen tudomásunk, ezért nemes egyszerűséggel a + Local-Master-Channel nevéből származtatjuk úgy, hogy a + Local-Master-Channel utolsó karakteréhez hozzáadunk 1-et. Pl. ha a + master neve "Local/0612463433@default-0299,1", akkor a slave neve + "Local/0612463433@default-0299,2" lesz. + + Ha igen, akkor regisztrál egy másik Hangup eventet, ami a + Local-Slave-Channel alapján figyeli a Hangup-ot. Ez a Hangup esemény + az eredeti előtt érkezik és kétesélyes a jelentése. Vagy a jó Cause + kód van benne, ha a hívás nem épült fel. Vagy pedig ha a hívás + felépül, akkor a felépülés pillanatában a 16-os (Normal Clearing) kód + érkezik. Ebben az esetben a Hangup eseményt nem kell komolyan venni, + hisz a hívás épp csak most kezdődött el. + + Éppen ezért a Hangup tényét az eredeti esemény (ori->event_hangup) + figyeli, a helyes Cause kódot pedig a most regisztrált esemény + (ori->event_hangup2). + + Az ori->event_hangup2 callback a Cause és Cause-txt értékeket + elmenti az ori->hangupcause és ori->hangupcausetxt változókba. Az + ori->event_hangup callback megvizsgálja, hogy az ori->hangupcause be + van -e állítva. Ha igen, akkor azt veszi alapul, ha nem, akkor saját + maga állapítja meg a Cause és Cause-txt változókat. */ + if (ori->asterisk_version == ASTERISK14) { + if (strlen(channel) > 6 && !strncmp(channel, "Local/", 6)) { + strncpy(ori->local_slave_channel, channel, sizeof(ori->local_slave_channel) - 1); + ori->local_slave_channel[strlen(ori->local_slave_channel)-1]++; + con_debug("detected Local-Slave-Channel=%s for ASTERISK14 Hangup method", + ori->local_slave_channel); + + // második Hangup event regisztrálása + ori->event_hangup2 = ami_event_register( + ami, event_hangup2_cb, ori, + "Event: Hangup\nChannel: %s", + ori->local_slave_channel + ); + } + } + + /* Ha van channel, akkor már nem kell az "Event: + OriginateResponse\nResponse: Failure", mert ha a hívás nem is + sikerül, attól még biztosan lesz Hangup. Azért mondjuk le az + OriginateResponse eseményt, mert ha Ringing közben történik a + Hangup, akkor az OriginateResponse Failure is megtörténik, ami akkor + már nem érdekes. Más szavakkal mondva: ha eddig a pontig nem + érkezett OriginateResponse Failure, akkor a továbbiakban nem is + veszünk róla tudomást, hisz a Hangup értesít minket a hívás + sikertelenségéről vagy a befejezéséről */ + if (ori->event_originateresponse_failure) { + ami_event_unregister(ami, ori->event_originateresponse_failure); + ori->event_originateresponse_failure = NULL; + } + strncpy(ori->channel, channel, sizeof(ori->channel) - 1); + ori->event_newstate = ami_event_register( + ami, got_ami_event, ori, + "Event: Newstate\nChannel: %s", + channel + ); + } +} + +static void event_gotuuid_cb (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + char *uniqueid = ami_getvar(event, "Uniqueid"); + char *channel = ami_getvar(event, "Channel"); + + con_debug("got UniqueID=%s, Channel=%s via VarSet method", uniqueid, channel); + got_uniqueid_and_channel(ori, uniqueid, channel); +} + +// Asterisk 1.4 esetén innen tudjuk meg a Channel-t +static void event_newcallerid_cb (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + + char *channel = ami_getvar(event, "Channel"); + con_debug("got Channel=%s via Newcallerid method", channel); + got_uniqueid_and_channel(ori, NULL, channel); +} + +static void response_originate (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + ami_t *ami = ori->ami; + +#ifdef AMI_ORIGINATE_DUMP_EVENTS + ami_event_dump(event); +#endif + + char *uniqueid = ami_getvar(event, "Uniqueid"); + // Ha itt szerepel az UniqueID, akkor Asterisk 1.4-ről van szó + if (strlen(uniqueid)) { + con_debug("got UniqueID=%s via Originate Response method", uniqueid); + ori->asterisk_version = ASTERISK14; + got_uniqueid_and_channel(ori, uniqueid, NULL); + ami_event_unregister(ami, ori->event_varset_uuid); + ori->event_varset_uuid = NULL; + ori->event_newcallerid = ami_event_register(ami, event_newcallerid_cb, ori, + "Event: Newcallerid\nUniqueid: %s", + uniqueid); + } +} + +static void event_originateresponse_failure_cb (ami_event_t *event) { + ori_t *ori = (ori_t*)event->userdata; + + ori->state = ORI_HANGUP; + ori->hangupcause = 0; + strncpy(ori->hangupcausetxt, "Origination failure", sizeof(ori->hangupcausetxt) - 1); + invoke_callback(ori, NULL); + + char *reason = ami_getvar(event, "Reason"); + con_debug("Action Originate failed, Reason=%s", reason); + printf("debdebdeb reason = %s\n", reason); +} + +// megrendelt AMI eventek lemondása +// TODO: az összeset felvenni ide! +void cleanup_events (ori_t *ori) { + ami_t *ami = ori->ami; + + ami_event_unregister(ami, ori->event_varset_uuid); + ori->event_varset_uuid = NULL; + + ami_event_unregister(ami, ori->action_originate); + ori->action_originate = NULL; +} + +ori_t *ami_originate (ami_t *ami, void *callback, void *userdata, const char *fmt, ...) { + ori_t *ori = malloc(sizeof(*ori)); + if (ori == NULL) { + con_debug("ami_originate() returned NULL"); + return NULL; + } + bzero(ori, sizeof(*ori)); // minden NULL + + ori->ami = ami; + ori->asterisk_version = ASTERISK16; + + char buf[AMI_BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + buf[AMI_BUFSIZ-1] = '\0'; // védelem + + // ami_originate_id a Varset módszerhez + generate_oid(ori->oid, sizeof(ori->oid)); + + ori->callback = callback; + ori->userdata = userdata; + + ori->event_varset_uuid = ami_event_register(ori->ami, event_gotuuid_cb, ori, + "Event: VarSet\n" + "Variable: ami_originate_id\n" + "Value: %s\n" + , ori->oid + ); + + con_debug("sending originate with id %s", ori->oid); + ori->action_originate = ami_action(ami, response_originate, ori, + "Action: Originate\n" + "Async: 1\n" + "Variable: ami_originate_id=%s\n" + "%s" + , ori->oid, buf + ); + + /* Az "Event: OriginateResponse" egy speciális Asterisk esemény. Talán + ez az egyetlen olyan esemény, ami konkrétan egy Action hatására jön létre + és rendelkezik ActionID-vel. Ezt az eseményt az "Action: + Originate\nAsync: 1" okozza akkor, amikor az originate során az első + csatorna csatlakozik vagy hibába esik. Ez az esemény hordozza a Channel + és UniqueID változókat, de nem innen vesszük őket, mert mire ez az + esemény megérkezik, addigra már más események (pl. Ringing) rég + megtörténtek. Viszont ha az Originate azonnal kudarcba fullad (pl. nem + létező Channel Technology megadása esetén), akkor erről a tényről csak + ezen a ponton tudunk értesülni. */ + ori->event_originateresponse_failure = ami_event_register( + ami, event_originateresponse_failure_cb, ori, + "Event: OriginateResponse\nResponse: Failure\nActionID: %d", + ori->action_originate->action_id + ); + + return ori; +} + +void ami_originate_destroy (ori_t *ori) { + // TODO: megírni:) +} + diff --git a/ami_test_client/originate.h b/ami_test_client/originate.h new file mode 100644 index 0000000000..d51652a2c5 --- /dev/null +++ b/ami_test_client/originate.h @@ -0,0 +1,49 @@ +/* originate.h + * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef ORIGINATE_H_INCLUDED +#define ORIGINATE_H_INCLUDED + +#include "ami.h" + +typedef struct ori_t { + ami_t *ami; + char oid[16]; // ori objektum saját egyedi azonosítója VarSet-hez + char uniqueid[48]; // Asterisk híváshoz rendelt Uniqueid + char channel[48]; + char local_slave_channel[48]; // Asterisk 1.4 esetén Local csatorna másik neve + void (*callback)(void*,ami_event_t*); + void *userdata; + int hangupcause; // ide került a bontás kódja + char hangupcausetxt[64]; // ide kerül a bontás szövege + ami_event_t *ami_event; + ami_event_list_t *action_originate; + ami_event_list_t *event_varset_uuid; + ami_event_list_t *event_newstate; + ami_event_list_t *event_newcallerid; // Asterisk 1.4-nél innen tudjuk meg a csatornát + ami_event_list_t *event_hangup; + ami_event_list_t *event_hangup2; // Asterisk 1.4 hangup cause-code érzékelés + ami_event_list_t *event_originateresponse_failure; // itt figyeljük, ha az Originate kudarcba fullad + enum { + ASTERISK14, + ASTERISK16, + } asterisk_version; + enum { + ORI_UNKNOWN = 0, + ORI_ANSWERED = 1, + ORI_HANGUP, + ORI_DIALING, + ORI_RINGING, + } state; // esemény típusa, hívás állapota, TODO: megfogalmazni rendesen +} ori_t; + +ori_t *ami_originate (ami_t *ami, void *callback, void *userdata, const char *fmt, ...); + +void ami_originate_free (ori_t *ori); + +#endif // #ifndef ORIGINATE_H_INCLUDED diff --git a/ami_test_client/volcon_config.h b/ami_test_client/volcon_config.h new file mode 100644 index 0000000000..f2324b2b9f --- /dev/null +++ b/ami_test_client/volcon_config.h @@ -0,0 +1,4 @@ +#define CONFIG_CARD "CA0106" +#define CONFIG_SELEM "Master" + +const char *watch_extens[] = {"1212", "1220", "1222", 0};