From 8a0e113e07d4d2e7a1f097d1f38a97baff759876 Mon Sep 17 00:00:00 2001 From: SunBeau Date: Tue, 30 Apr 2024 09:44:15 +0800 Subject: [PATCH] regenerate docs --- doc/html/bc_sd.png | Bin 635 -> 0 bytes doc/html/bdwn.png | Bin 0 -> 147 bytes .../dir_0b61c55142250c0cc363383afd8075a4.html | 44 +- .../dir_40c73bf2095fd0473b1b85ccdcac2768.html | 43 +- .../dir_40c73bf2095fd0473b1b85ccdcac2768.js | 3 +- .../dir_7b5d38f1875f1b693f62ca6a108a1129.html | 44 +- .../dir_b94456e2763cc4ff61641ee1fd517631.html | 30 +- .../dir_d44c64559bbebec7f509842c48db8b23.html | 30 +- .../dir_f8363776597ea1205a0bcfd931cbea00.html | 32 +- doc/html/doc.png | Bin 0 -> 746 bytes doc/html/doc.svg | 12 - doc/html/docd.svg | 12 - doc/html/doxygen.css | 1054 ++--- doc/html/doxygen.svg | 4 +- doc/html/dynsections.js | 4 +- doc/html/files.html | 79 +- doc/html/folderclosed.png | Bin 0 -> 616 bytes doc/html/folderclosed.svg | 11 - doc/html/folderclosedd.svg | 11 - doc/html/folderopen.png | Bin 0 -> 597 bytes doc/html/folderopen.svg | 17 - doc/html/folderopend.svg | 12 - doc/html/globals.html | 56 +- doc/html/globals_a.html | 27 +- doc/html/globals_b.html | 28 +- doc/html/globals_c.html | 37 +- doc/html/globals_d.html | 31 +- doc/html/globals_defs.html | 27 +- doc/html/globals_e.html | 47 +- doc/html/globals_f.html | 34 +- doc/html/globals_func.html | 58 +- doc/html/globals_func_a.html | 29 +- doc/html/globals_func_b.html | 30 +- doc/html/globals_func_c.html | 39 +- doc/html/globals_func_d.html | 29 +- doc/html/globals_func_e.html | 49 +- doc/html/globals_func_f.html | 36 +- doc/html/globals_func_g.html | 43 +- doc/html/globals_func_h.html | 29 +- doc/html/globals_func_n.html | 41 +- doc/html/globals_func_o.html | 31 +- doc/html/globals_func_p.html | 38 +- doc/html/globals_func_q.html | 1100 +++-- doc/html/globals_func_r.html | 66 +- doc/html/globals_func_s.html | 62 +- doc/html/globals_func_w.html | 34 +- doc/html/globals_g.html | 41 +- doc/html/globals_h.html | 27 +- doc/html/globals_n.html | 39 +- doc/html/globals_o.html | 29 +- doc/html/globals_p.html | 36 +- doc/html/globals_q.html | 1098 +++-- doc/html/globals_r.html | 64 +- doc/html/globals_s.html | 60 +- doc/html/globals_w.html | 32 +- doc/html/index.html | 41 +- doc/html/index.js | 9 - doc/html/jquery.js | 11 +- doc/html/menu.js | 97 +- doc/html/nav_fd.png | Bin 169 -> 0 bytes doc/html/nav_hd.png | Bin 114 -> 0 bytes doc/html/navtree.css | 22 +- doc/html/navtree.js | 16 +- doc/html/navtreedata.js | 11 +- doc/html/navtreeindex0.js | 284 +- doc/html/navtreeindex1.js | 256 +- doc/html/qaconf_8c.html | 126 +- doc/html/qaconf_8c.js | 4 +- doc/html/qaconf_8c_source.html | 2098 +++++----- doc/html/qconfig_8c.html | 94 +- doc/html/qconfig_8c.js | 5 +- doc/html/qconfig_8c_source.html | 902 ++--- doc/html/qcount_8c.html | 58 +- doc/html/qcount_8c_source.html | 294 +- doc/html/qdatabase_8c.js | 27 - doc/html/qdatabase_8c_source.html | 971 ----- ...abase_8c.html => qdatabase__mysql_8c.html} | 332 +- doc/html/qdatabase__mysql_8c.js | 27 + doc/html/qdatabase__mysql_8c_source.html | 971 +++++ doc/html/qdatabase__pgsql_8c.html | 1271 ++++++ doc/html/qdatabase__pgsql_8c.js | 28 + doc/html/qdatabase__pgsql_8c_source.html | 1130 ++++++ doc/html/qencode_8c.html | 120 +- doc/html/qencode_8c.js | 8 +- doc/html/qencode_8c_source.html | 950 ++--- doc/html/qfile_8c.html | 198 +- doc/html/qfile_8c.js | 15 +- doc/html/qfile_8c_source.html | 1152 +++--- doc/html/qgrow_8c.html | 118 +- doc/html/qgrow_8c.js | 6 +- doc/html/qgrow_8c_source.html | 670 +-- doc/html/qhash_8c.html | 100 +- doc/html/qhash_8c_source.html | 926 ++--- doc/html/qhasharr_8c.html | 231 +- doc/html/qhasharr_8c.js | 10 +- doc/html/qhasharr_8c_source.html | 2276 +++++------ doc/html/qhashtbl_8c.html | 160 +- doc/html/qhashtbl_8c.js | 6 +- doc/html/qhashtbl_8c_source.html | 1490 +++---- doc/html/qhttpclient_8c.html | 623 +-- doc/html/qhttpclient_8c.js | 26 +- doc/html/qhttpclient_8c_source.html | 3590 ++++++++--------- doc/html/qio_8c.html | 98 +- doc/html/qio_8c.js | 1 + doc/html/qio_8c_source.html | 710 ++-- doc/html/qlibc_8h.html | 26 +- doc/html/qlibc_8h_source.html | 156 +- doc/html/qlibcext_8h.html | 26 +- doc/html/qlibcext_8h_source.html | 116 +- doc/html/qlist_8c.html | 242 +- doc/html/qlist_8c.js | 18 +- doc/html/qlist_8c_source.html | 1978 ++++----- doc/html/qlisttbl_8c.html | 196 +- doc/html/qlisttbl_8c.js | 8 +- doc/html/qlisttbl_8c_source.html | 2398 +++++------ doc/html/qlog_8c.html | 74 +- doc/html/qlog_8c.js | 2 +- doc/html/qlog_8c_source.html | 716 ++-- doc/html/qqueue_8c.html | 170 +- doc/html/qqueue_8c.js | 14 +- doc/html/qqueue_8c_source.html | 970 ++--- doc/html/qsem_8c.html | 104 +- doc/html/qsem_8c_source.html | 652 +-- doc/html/qshm_8c.html | 70 +- doc/html/qshm_8c.js | 2 +- doc/html/qshm_8c_source.html | 382 +- doc/html/qsocket_8c.html | 56 +- doc/html/qsocket_8c.js | 2 +- doc/html/qsocket_8c_source.html | 384 +- doc/html/qstack_8c.html | 176 +- doc/html/qstack_8c.js | 14 +- doc/html/qstack_8c_source.html | 970 ++--- doc/html/qstring_8c.html | 316 +- doc/html/qstring_8c.js | 40 +- doc/html/qstring_8c_source.html | 1880 ++++----- doc/html/qsystem_8c.html | 50 +- doc/html/qsystem_8c.js | 4 +- doc/html/qsystem_8c_source.html | 196 +- doc/html/qtime_8c.html | 205 +- doc/html/qtime_8c.js | 15 +- doc/html/qtime_8c_source.html | 524 +-- doc/html/qtokenbucket_8c.html | 56 +- doc/html/qtokenbucket_8c_source.html | 302 +- doc/html/qtreetbl_8c.html | 275 +- doc/html/qtreetbl_8c.js | 13 +- doc/html/qtreetbl_8c_source.html | 2794 ++++++------- doc/html/qvector_8c.html | 288 +- doc/html/qvector_8c.js | 18 +- doc/html/qvector_8c_source.html | 2018 ++++----- doc/html/resize.js | 97 +- doc/html/splitbard.png | Bin 282 -> 0 bytes doc/html/tab_ad.png | Bin 135 -> 0 bytes doc/html/tab_bd.png | Bin 173 -> 0 bytes doc/html/tab_hd.png | Bin 180 -> 0 bytes doc/html/tab_sd.png | Bin 188 -> 0 bytes doc/html/tabs.css | 2 +- 156 files changed, 24697 insertions(+), 21756 deletions(-) delete mode 100644 doc/html/bc_sd.png create mode 100644 doc/html/bdwn.png create mode 100644 doc/html/doc.png delete mode 100644 doc/html/doc.svg delete mode 100644 doc/html/docd.svg create mode 100644 doc/html/folderclosed.png delete mode 100644 doc/html/folderclosed.svg delete mode 100644 doc/html/folderclosedd.svg create mode 100644 doc/html/folderopen.png delete mode 100644 doc/html/folderopen.svg delete mode 100644 doc/html/folderopend.svg delete mode 100644 doc/html/index.js delete mode 100644 doc/html/nav_fd.png delete mode 100644 doc/html/nav_hd.png delete mode 100644 doc/html/qdatabase_8c.js delete mode 100644 doc/html/qdatabase_8c_source.html rename doc/html/{qdatabase_8c.html => qdatabase__mysql_8c.html} (70%) create mode 100644 doc/html/qdatabase__mysql_8c.js create mode 100644 doc/html/qdatabase__mysql_8c_source.html create mode 100644 doc/html/qdatabase__pgsql_8c.html create mode 100644 doc/html/qdatabase__pgsql_8c.js create mode 100644 doc/html/qdatabase__pgsql_8c_source.html delete mode 100644 doc/html/splitbard.png delete mode 100644 doc/html/tab_ad.png delete mode 100644 doc/html/tab_bd.png delete mode 100644 doc/html/tab_hd.png delete mode 100644 doc/html/tab_sd.png diff --git a/doc/html/bc_sd.png b/doc/html/bc_sd.png deleted file mode 100644 index 31ca888dc71049713b35c351933a8d0f36180bf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 635 zcmV->0)+jEP)Jwi0r1~gdSq#w{Bu1q z`craw(p2!hu$4C_$Oc3X(sI6e=9QSTwPt{G) z=htT&^~&c~L2~e{r5_5SYe7#Is-$ln>~Kd%$F#tC65?{LvQ}8O`A~RBB0N~`2M+waajO;5>3B&-viHGJeEK2TQOiPRa zfDKyqwMc4wfaEh4jt>H`nW_Zidwk@Bowp`}(VUaj-pSI(-1L>FJVsX}Yl9~JsqgsZ zUD9(rMwf23Gez6KPa|wwInZodP-2}9@fK0Ga_9{8SOjU&4l`pH4@qlQp83>>HT$xW zER^U>)MyV%t(Lu=`d=Y?{k1@}&r7ZGkFQ%z%N+sE9BtYjovzxyxCPxN6&@wLK{soQ zSmkj$aLI}miuE^p@~4}mg9OjDfGEkgY4~^XzLRUBB*O{+&vq<3v(E%+k_i%=`~j%{ Vj14gnt9}3g002ovPDHLkV1n!oC4m3{ diff --git a/doc/html/bdwn.png b/doc/html/bdwn.png new file mode 100644 index 0000000000000000000000000000000000000000..940a0b950443a0bb1b216ac03c45b8a16c955452 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3HEvS)PKZC{Gv1kP61Pb5HX&C2wk~_T - + - - + + qLibc: containers Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,43 +52,44 @@
-
containers Directory Reference
+
+
containers Directory Reference
- - + - + - + - + - + - + - + - + - +

+

Files

 qgrow.c
file  qgrow.c [code]
 Grow container that handles growable objects.
 
 qhasharr.c
file  qhasharr.c [code]
 Static(array) hash-table implementation.
 
 qhashtbl.c
file  qhashtbl.c [code]
 Hash-table container implementation.
 
 qlist.c
file  qlist.c [code]
 Doubly Linked-list implementation.
 
 qlisttbl.c
file  qlisttbl.c [code]
 Linked-list-table implementation.
 
 qqueue.c
file  qqueue.c [code]
 Queue implementation.
 
 qstack.c
file  qstack.c [code]
 Stack implementation.
 
 qtreetbl.c
file  qtreetbl.c [code]
 Tree Table container that implements the Left-Leaning Red-Black BST algorithm.
 
 qvector.c
file  qvector.c [code]
 Vector container implementation.
 
@@ -99,7 +99,7 @@ diff --git a/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.html b/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.html index d7aee4f5..53562a35 100644 --- a/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.html +++ b/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,34 +52,38 @@
-
extensions Directory Reference
+
+
extensions Directory Reference
- - + - + - - + + + + + - + - + - +

+

Files

 qaconf.c
file  qaconf.c [code]
 Apache-style configuration file parser.
 
 qconfig.c
file  qconfig.c [code]
 INI-style configuration file parser.
 
 qdatabase.c
 Database wrapper.
file  qdatabase_mysql.c [code]
 Database wrapper.
 
file  qdatabase_pgsql.c [code]
 Database wrapper.
 
 qhttpclient.c
file  qhttpclient.c [code]
 HTTP client object.
 
 qlog.c
file  qlog.c [code]
 Rotating file logger object.
 
 qtokenbucket.c
file  qtokenbucket.c [code]
 Token Bucket implementation.
 
@@ -90,7 +93,7 @@ diff --git a/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.js b/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.js index 0e922d41..2cd1bddf 100644 --- a/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.js +++ b/doc/html/dir_40c73bf2095fd0473b1b85ccdcac2768.js @@ -2,7 +2,8 @@ var dir_40c73bf2095fd0473b1b85ccdcac2768 = [ [ "qaconf.c", "qaconf_8c.html", "qaconf_8c" ], [ "qconfig.c", "qconfig_8c.html", "qconfig_8c" ], - [ "qdatabase.c", "qdatabase_8c.html", "qdatabase_8c" ], + [ "qdatabase_mysql.c", "qdatabase__mysql_8c.html", "qdatabase__mysql_8c" ], + [ "qdatabase_pgsql.c", "qdatabase__pgsql_8c.html", "qdatabase__pgsql_8c" ], [ "qhttpclient.c", "qhttpclient_8c.html", "qhttpclient_8c" ], [ "qlog.c", "qlog_8c.html", "qlog_8c" ], [ "qtokenbucket.c", "qtokenbucket_8c.html", "qtokenbucket_8c" ] diff --git a/doc/html/dir_7b5d38f1875f1b693f62ca6a108a1129.html b/doc/html/dir_7b5d38f1875f1b693f62ca6a108a1129.html index d36faf12..382c06df 100644 --- a/doc/html/dir_7b5d38f1875f1b693f62ca6a108a1129.html +++ b/doc/html/dir_7b5d38f1875f1b693f62ca6a108a1129.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,43 +52,44 @@
-
utilities Directory Reference
+
+
utilities Directory Reference
- - + - + - + - + - + - + - + - + - +

+

Files

 qcount.c
file  qcount.c [code]
 Counter file handling APIs.
 
 qencode.c
file  qencode.c [code]
 Encoding/decoding APIs.
 
 qfile.c
file  qfile.c [code]
 File handling APIs.
 
 qhash.c
file  qhash.c [code]
 Hash APIs.
 
 qio.c
file  qio.c [code]
 I/O handling APIs.
 
 qsocket.c
file  qsocket.c [code]
 Socket dandling APIs.
 
 qstring.c
file  qstring.c [code]
 String APIs.
 
 qsystem.c
file  qsystem.c [code]
 System APIs.
 
 qtime.c
file  qtime.c [code]
 Time handling APIs.
 
@@ -99,7 +99,7 @@ diff --git a/doc/html/dir_b94456e2763cc4ff61641ee1fd517631.html b/doc/html/dir_b94456e2763cc4ff61641ee1fd517631.html index 3918fd1c..e49812f5 100644 --- a/doc/html/dir_b94456e2763cc4ff61641ee1fd517631.html +++ b/doc/html/dir_b94456e2763cc4ff61641ee1fd517631.html @@ -1,9 +1,9 @@ - + - - + + qLibc: ipc Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,22 +52,23 @@
-
ipc Directory Reference
+
+
ipc Directory Reference
- - + - +

+

Files

 qsem.c
file  qsem.c [code]
 Semaphore APIs.
 
 qshm.c
file  qshm.c [code]
 Shared-memory APIs.
 
@@ -78,7 +78,7 @@ diff --git a/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html b/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html index f03cbaa5..1535a01e 100644 --- a/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html +++ b/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include Directory Reference +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,20 +52,19 @@
-
include Directory Reference
+
+
include Directory Reference
- - -

+

Directories

 qlibc
 
@@ -74,7 +72,7 @@ diff --git a/doc/html/dir_f8363776597ea1205a0bcfd931cbea00.html b/doc/html/dir_f8363776597ea1205a0bcfd931cbea00.html index 73b2a712..ba866b94 100644 --- a/doc/html/dir_f8363776597ea1205a0bcfd931cbea00.html +++ b/doc/html/dir_f8363776597ea1205a0bcfd931cbea00.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include/qlibc Directory Reference +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include/qlibc Directory Reference @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,22 +52,23 @@
-
qlibc Directory Reference
+
+
qlibc Directory Reference
- - + - +

+

Files

 qlibc.h
file  qlibc.h [code]
 qlibc header file.
 
 qlibcext.h
file  qlibcext.h [code]
 qlibc extension header file.
 
@@ -78,7 +78,7 @@ diff --git a/doc/html/doc.png b/doc/html/doc.png new file mode 100644 index 0000000000000000000000000000000000000000..17edabff95f7b8da13c9516a04efe05493c29501 GIT binary patch literal 746 zcmV7=@pnbNXRFEm&G8P!&WHG=d)>K?YZ1bzou)2{$)) zumDct!>4SyxL;zgaG>wy`^Hv*+}0kUfCrz~BCOViSb$_*&;{TGGn2^x9K*!Sf0=lV zpP=7O;GA0*Jm*tTYj$IoXvimpnV4S1Z5f$p*f$Db2iq2zrVGQUz~yq`ahn7ck(|CE z7Gz;%OP~J6)tEZWDzjhL9h2hdfoU2)Nd%T<5Kt;Y0XLt&<@6pQx!nw*5`@bq#?l*?3z{Hlzoc=Pr>oB5(9i6~_&-}A(4{Q$>c>%rV&E|a(r&;?i5cQB=} zYSDU5nXG)NS4HEs0it2AHe2>shCyr7`6@4*6{r@8fXRbTA?=IFVWAQJL&H5H{)DpM#{W(GL+Idzf^)uRV@oB8u$ z8v{MfJbTiiRg4bza<41NAzrl{=3fl_D+$t+^!xlQ8S}{UtY`e z;;&9UhyZqQRN%2pot{*Ei0*4~hSF_3AH2@fKU!$NSflS>{@tZpDT4`M2WRTTVH+D? z)GFlEGGHe?koB}i|1w45!BF}N_q&^HJ&-tyR{(afC6H7|aml|tBBbv}55C5DNP8p3 z)~jLEO4Z&2hZmP^i-e%(@d!(E|KRafiU8Q5u(wU((j8un3OR*Hvj+t literal 0 HcmV?d00001 diff --git a/doc/html/doc.svg b/doc/html/doc.svg deleted file mode 100644 index 0b928a53..00000000 --- a/doc/html/doc.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/doc/html/docd.svg b/doc/html/docd.svg deleted file mode 100644 index ac18b275..00000000 --- a/doc/html/docd.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/doc/html/doxygen.css b/doc/html/doxygen.css index 0caa19b2..ffbff022 100644 --- a/doc/html/doxygen.css +++ b/doc/html/doxygen.css @@ -1,368 +1,29 @@ -/* The standard CSS for doxygen 1.9.7*/ - -html { -/* page base colors */ ---page-background-color: white; ---page-foreground-color: black; ---page-link-color: #3D578C; ---page-visited-link-color: #4665A2; - -/* index */ ---index-odd-item-bg-color: #F8F9FC; ---index-even-item-bg-color: white; ---index-header-color: black; ---index-separator-color: #A0A0A0; - -/* header */ ---header-background-color: #F9FAFC; ---header-separator-color: #C4CFE5; ---header-gradient-image: url('nav_h.png'); ---group-header-separator-color: #879ECB; ---group-header-color: #354C7B; ---inherit-header-color: gray; - ---footer-foreground-color: #2A3D61; ---footer-logo-width: 104px; ---citation-label-color: #334975; ---glow-color: cyan; - ---title-background-color: white; ---title-separator-color: #5373B4; ---directory-separator-color: #9CAFD4; ---separator-color: #4A6AAA; - ---blockquote-background-color: #F7F8FB; ---blockquote-border-color: #9CAFD4; - ---scrollbar-thumb-color: #9CAFD4; ---scrollbar-background-color: #F9FAFC; - ---icon-background-color: #728DC1; ---icon-foreground-color: white; ---icon-doc-image: url('doc.svg'); ---icon-folder-open-image: url('folderopen.svg'); ---icon-folder-closed-image: url('folderclosed.svg'); - -/* brief member declaration list */ ---memdecl-background-color: #F9FAFC; ---memdecl-separator-color: #DEE4F0; ---memdecl-foreground-color: #555; ---memdecl-template-color: #4665A2; - -/* detailed member list */ ---memdef-border-color: #A8B8D9; ---memdef-title-background-color: #E2E8F2; ---memdef-title-gradient-image: url('nav_f.png'); ---memdef-proto-background-color: #DFE5F1; ---memdef-proto-text-color: #253555; ---memdef-proto-text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); ---memdef-doc-background-color: white; ---memdef-param-name-color: #602020; ---memdef-template-color: #4665A2; - -/* tables */ ---table-cell-border-color: #2D4068; ---table-header-background-color: #374F7F; ---table-header-foreground-color: #FFFFFF; - -/* labels */ ---label-background-color: #728DC1; ---label-left-top-border-color: #5373B4; ---label-right-bottom-border-color: #C4CFE5; ---label-foreground-color: white; - -/** navigation bar/tree/menu */ ---nav-background-color: #F9FAFC; ---nav-foreground-color: #364D7C; ---nav-gradient-image: url('tab_b.png'); ---nav-gradient-hover-image: url('tab_h.png'); ---nav-gradient-active-image: url('tab_a.png'); ---nav-gradient-active-image-parent: url("../tab_a.png"); ---nav-separator-image: url('tab_s.png'); ---nav-breadcrumb-image: url('bc_s.png'); ---nav-breadcrumb-border-color: #C2CDE4; ---nav-splitbar-image: url('splitbar.png'); ---nav-font-size-level1: 13px; ---nav-font-size-level2: 10px; ---nav-font-size-level3: 9px; ---nav-text-normal-color: #283A5D; ---nav-text-hover-color: white; ---nav-text-active-color: white; ---nav-text-normal-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); ---nav-text-hover-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); ---nav-text-active-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); ---nav-menu-button-color: #364D7C; ---nav-menu-background-color: white; ---nav-menu-foreground-color: #555555; ---nav-menu-toggle-color: rgba(255, 255, 255, 0.5); ---nav-arrow-color: #9CAFD4; ---nav-arrow-selected-color: #9CAFD4; - -/* table of contents */ ---toc-background-color: #F4F6FA; ---toc-border-color: #D8DFEE; ---toc-header-color: #4665A2; ---toc-down-arrow-image: url("data:image/svg+xml;utf8,&%238595;"); - -/** search field */ ---search-background-color: white; ---search-foreground-color: #909090; ---search-magnification-image: url('mag.svg'); ---search-magnification-select-image: url('mag_sel.svg'); ---search-active-color: black; ---search-filter-background-color: #F9FAFC; ---search-filter-foreground-color: black; ---search-filter-border-color: #90A5CE; ---search-filter-highlight-text-color: white; ---search-filter-highlight-bg-color: #3D578C; ---search-results-foreground-color: #425E97; ---search-results-background-color: #EEF1F7; ---search-results-border-color: black; ---search-box-shadow: inset 0.5px 0.5px 3px 0px #555; - -/** code fragments */ ---code-keyword-color: #008000; ---code-type-keyword-color: #604020; ---code-flow-keyword-color: #E08000; ---code-comment-color: #800000; ---code-preprocessor-color: #806020; ---code-string-literal-color: #002080; ---code-char-literal-color: #008080; ---code-xml-cdata-color: black; ---code-vhdl-digit-color: #FF00FF; ---code-vhdl-char-color: #000000; ---code-vhdl-keyword-color: #700070; ---code-vhdl-logic-color: #FF0000; ---code-link-color: #4665A2; ---code-external-link-color: #4665A2; ---fragment-foreground-color: black; ---fragment-background-color: #FBFCFD; ---fragment-border-color: #C4CFE5; ---fragment-lineno-border-color: #00FF00; ---fragment-lineno-background-color: #E8E8E8; ---fragment-lineno-foreground-color: black; ---fragment-lineno-link-fg-color: #4665A2; ---fragment-lineno-link-bg-color: #D8D8D8; ---fragment-lineno-link-hover-fg-color: #4665A2; ---fragment-lineno-link-hover-bg-color: #C8C8C8; ---tooltip-foreground-color: black; ---tooltip-background-color: white; ---tooltip-border-color: gray; ---tooltip-doc-color: grey; ---tooltip-declaration-color: #006318; ---tooltip-link-color: #4665A2; ---tooltip-shadow: 1px 1px 7px gray; - -/** font-family */ ---font-family-normal: Roboto,sans-serif; ---font-family-monospace: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; ---font-family-nav: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; ---font-family-title: Tahoma,Arial,sans-serif; ---font-family-toc: Verdana,'DejaVu Sans',Geneva,sans-serif; ---font-family-search: Arial,Verdana,sans-serif; ---font-family-icon: Arial,Helvetica; ---font-family-tooltip: Roboto,sans-serif; - -} - -@media (prefers-color-scheme: dark) { - html:not(.dark-mode) { - color-scheme: dark; - -/* page base colors */ ---page-background-color: black; ---page-foreground-color: #C9D1D9; ---page-link-color: #90A5CE; ---page-visited-link-color: #A3B4D7; - -/* index */ ---index-odd-item-bg-color: #0B101A; ---index-even-item-bg-color: black; ---index-header-color: #C4CFE5; ---index-separator-color: #334975; - -/* header */ ---header-background-color: #070B11; ---header-separator-color: #141C2E; ---header-gradient-image: url('nav_hd.png'); ---group-header-separator-color: #283A5D; ---group-header-color: #90A5CE; ---inherit-header-color: #A0A0A0; - ---footer-foreground-color: #5B7AB7; ---footer-logo-width: 60px; ---citation-label-color: #90A5CE; ---glow-color: cyan; - ---title-background-color: #090D16; ---title-separator-color: #354C79; ---directory-separator-color: #283A5D; ---separator-color: #283A5D; - ---blockquote-background-color: #101826; ---blockquote-border-color: #283A5D; - ---scrollbar-thumb-color: #283A5D; ---scrollbar-background-color: #070B11; - ---icon-background-color: #334975; ---icon-foreground-color: #C4CFE5; ---icon-doc-image: url('docd.svg'); ---icon-folder-open-image: url('folderopend.svg'); ---icon-folder-closed-image: url('folderclosedd.svg'); - -/* brief member declaration list */ ---memdecl-background-color: #0B101A; ---memdecl-separator-color: #2C3F65; ---memdecl-foreground-color: #BBB; ---memdecl-template-color: #7C95C6; - -/* detailed member list */ ---memdef-border-color: #233250; ---memdef-title-background-color: #1B2840; ---memdef-title-gradient-image: url('nav_fd.png'); ---memdef-proto-background-color: #19243A; ---memdef-proto-text-color: #9DB0D4; ---memdef-proto-text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.9); ---memdef-doc-background-color: black; ---memdef-param-name-color: #D28757; ---memdef-template-color: #7C95C6; - -/* tables */ ---table-cell-border-color: #283A5D; ---table-header-background-color: #283A5D; ---table-header-foreground-color: #C4CFE5; - -/* labels */ ---label-background-color: #354C7B; ---label-left-top-border-color: #4665A2; ---label-right-bottom-border-color: #283A5D; ---label-foreground-color: #CCCCCC; - -/** navigation bar/tree/menu */ ---nav-background-color: #101826; ---nav-foreground-color: #364D7C; ---nav-gradient-image: url('tab_bd.png'); ---nav-gradient-hover-image: url('tab_hd.png'); ---nav-gradient-active-image: url('tab_ad.png'); ---nav-gradient-active-image-parent: url("../tab_ad.png"); ---nav-separator-image: url('tab_sd.png'); ---nav-breadcrumb-image: url('bc_sd.png'); ---nav-breadcrumb-border-color: #2A3D61; ---nav-splitbar-image: url('splitbard.png'); ---nav-font-size-level1: 13px; ---nav-font-size-level2: 10px; ---nav-font-size-level3: 9px; ---nav-text-normal-color: #B6C4DF; ---nav-text-hover-color: #DCE2EF; ---nav-text-active-color: #DCE2EF; ---nav-text-normal-shadow: 0px 1px 1px black; ---nav-text-hover-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); ---nav-text-active-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); ---nav-menu-button-color: #B6C4DF; ---nav-menu-background-color: #05070C; ---nav-menu-foreground-color: #BBBBBB; ---nav-menu-toggle-color: rgba(255, 255, 255, 0.2); ---nav-arrow-color: #334975; ---nav-arrow-selected-color: #90A5CE; - -/* table of contents */ ---toc-background-color: #151E30; ---toc-border-color: #202E4A; ---toc-header-color: #A3B4D7; ---toc-down-arrow-image: url("data:image/svg+xml;utf8,&%238595;"); - -/** search field */ ---search-background-color: black; ---search-foreground-color: #C5C5C5; ---search-magnification-image: url('mag_d.svg'); ---search-magnification-select-image: url('mag_seld.svg'); ---search-active-color: #C5C5C5; ---search-filter-background-color: #101826; ---search-filter-foreground-color: #90A5CE; ---search-filter-border-color: #7C95C6; ---search-filter-highlight-text-color: #BCC9E2; ---search-filter-highlight-bg-color: #283A5D; ---search-results-background-color: #101826; ---search-results-foreground-color: #90A5CE; ---search-results-border-color: #7C95C6; ---search-box-shadow: inset 0.5px 0.5px 3px 0px #2F436C; - -/** code fragments */ ---code-keyword-color: #CC99CD; ---code-type-keyword-color: #AB99CD; ---code-flow-keyword-color: #E08000; ---code-comment-color: #717790; ---code-preprocessor-color: #65CABE; ---code-string-literal-color: #7EC699; ---code-char-literal-color: #00E0F0; ---code-xml-cdata-color: #C9D1D9; ---code-vhdl-digit-color: #FF00FF; ---code-vhdl-char-color: #000000; ---code-vhdl-keyword-color: #700070; ---code-vhdl-logic-color: #FF0000; ---code-link-color: #79C0FF; ---code-external-link-color: #79C0FF; ---fragment-foreground-color: #C9D1D9; ---fragment-background-color: black; ---fragment-border-color: #30363D; ---fragment-lineno-border-color: #30363D; ---fragment-lineno-background-color: black; ---fragment-lineno-foreground-color: #6E7681; ---fragment-lineno-link-fg-color: #6E7681; ---fragment-lineno-link-bg-color: #303030; ---fragment-lineno-link-hover-fg-color: #8E96A1; ---fragment-lineno-link-hover-bg-color: #505050; ---tooltip-foreground-color: #C9D1D9; ---tooltip-background-color: #202020; ---tooltip-border-color: #C9D1D9; ---tooltip-doc-color: #D9E1E9; ---tooltip-declaration-color: #20C348; ---tooltip-link-color: #79C0FF; ---tooltip-shadow: none; - -/** font-family */ ---font-family-normal: Roboto,sans-serif; ---font-family-monospace: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; ---font-family-nav: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; ---font-family-title: Tahoma,Arial,sans-serif; ---font-family-toc: Verdana,'DejaVu Sans',Geneva,sans-serif; ---font-family-search: Arial,Verdana,sans-serif; ---font-family-icon: Arial,Helvetica; ---font-family-tooltip: Roboto,sans-serif; - -}} -body { - background-color: var(--page-background-color); - color: var(--page-foreground-color); -} +/* The standard CSS for doxygen 1.9.1 */ body, table, div, p, dl { - font-weight: 400; - font-size: 14px; - font-family: var(--font-family-normal); - line-height: 22px; + font: 400 14px/22px Roboto,sans-serif; +} + +p.reference, p.definition { + font: 400 14px/22px Roboto,sans-serif; } /* @group Heading Levels */ -.title { - font-weight: 400; - font-size: 14px; - font-family: var(--font-family-normal); - line-height: 28px; +h1.groupheader { font-size: 150%; - font-weight: bold; - margin: 10px 2px; } -h1.groupheader { +.title { + font: 400 14px/28px Roboto,sans-serif; font-size: 150%; + font-weight: bold; + margin: 10px 2px; } h2.groupheader { - border-bottom: 1px solid var(--group-header-separator-color); - color: var(--group-header-color); + border-bottom: 1px solid #879ECB; + color: #354C7B; font-size: 150%; font-weight: normal; margin-top: 1.75em; @@ -385,13 +46,22 @@ h1, h2, h3, h4, h5, h6 { } h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { - text-shadow: 0 0 15px var(--glow-color); + text-shadow: 0 0 15px cyan; } dt { font-weight: bold; } +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; +} + p.startli, p.startdd { margin-top: 2px; } @@ -443,6 +113,7 @@ h3.version { } div.navtab { + border-right: 1px solid #A3B4D7; padding-right: 15px; text-align: right; line-height: 110%; @@ -456,17 +127,16 @@ td.navtab { padding-right: 6px; padding-left: 6px; } - td.navtabHL { - background-image: var(--nav-gradient-active-image); + background-image: url('tab_a.png'); background-repeat:repeat-x; padding-right: 6px; padding-left: 6px; } td.navtabHL a, td.navtabHL a:visited { - color: var(--nav-text-hover-color); - text-shadow: var(--nav-text-hover-shadow); + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); } a.navtab { @@ -478,7 +148,7 @@ div.qindex{ width: 100%; line-height: 140%; font-size: 130%; - color: var(--index-separator-color); + color: #A0A0A0; } dt.alphachar{ @@ -487,7 +157,7 @@ dt.alphachar{ } .alphachar a{ - color: var(--index-header-color); + color: black; } .alphachar a:hover, .alphachar a:visited{ @@ -506,12 +176,8 @@ dt.alphachar{ line-height: 1.15em; } -.classindex dl.even { - background-color: var(--index-even-item-bg-color); -} - .classindex dl.odd { - background-color: var(--index-odd-item-bg-color); + background-color: #F8F9FC; } @media(min-width: 1120px) { @@ -530,19 +196,23 @@ dt.alphachar{ /* @group Link Styling */ a { - color: var(--page-link-color); + color: #3D578C; font-weight: normal; text-decoration: none; } .contents a:visited { - color: var(--page-visited-link-color); + color: #4665A2; } a:hover { text-decoration: underline; } +.contents a.qindexHL:visited { + color: #FFFFFF; +} + a.el { font-weight: bold; } @@ -551,39 +221,12 @@ a.elRef { } a.code, a.code:visited, a.line, a.line:visited { - color: var(--code-link-color); + color: #4665A2; } a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { - color: var(--code-external-link-color); -} - -a.code.hl_class { /* style for links to class names in code snippets */ } -a.code.hl_struct { /* style for links to struct names in code snippets */ } -a.code.hl_union { /* style for links to union names in code snippets */ } -a.code.hl_interface { /* style for links to interface names in code snippets */ } -a.code.hl_protocol { /* style for links to protocol names in code snippets */ } -a.code.hl_category { /* style for links to category names in code snippets */ } -a.code.hl_exception { /* style for links to exception names in code snippets */ } -a.code.hl_service { /* style for links to service names in code snippets */ } -a.code.hl_singleton { /* style for links to singleton names in code snippets */ } -a.code.hl_concept { /* style for links to concept names in code snippets */ } -a.code.hl_namespace { /* style for links to namespace names in code snippets */ } -a.code.hl_package { /* style for links to package names in code snippets */ } -a.code.hl_define { /* style for links to macro names in code snippets */ } -a.code.hl_function { /* style for links to function names in code snippets */ } -a.code.hl_variable { /* style for links to variable names in code snippets */ } -a.code.hl_typedef { /* style for links to typedef names in code snippets */ } -a.code.hl_enumvalue { /* style for links to enum value names in code snippets */ } -a.code.hl_enumeration { /* style for links to enumeration names in code snippets */ } -a.code.hl_signal { /* style for links to Qt signal names in code snippets */ } -a.code.hl_slot { /* style for links to Qt slot names in code snippets */ } -a.code.hl_friend { /* style for links to friend names in code snippets */ } -a.code.hl_dcop { /* style for links to KDE3 DCOP names in code snippets */ } -a.code.hl_property { /* style for links to property names in code snippets */ } -a.code.hl_event { /* style for links to event names in code snippets */ } -a.code.hl_sequence { /* style for links to sequence names in code snippets */ } -a.code.hl_dictionary { /* style for links to dictionary names in code snippets */ } + color: #4665A2; +} /* @end */ @@ -592,17 +235,7 @@ dl.el { } ul { - overflow: visible; -} - -ul.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; - column-count: 3; - list-style-type: none; + overflow: hidden; /*Fixed: list item bullets overlap floating elements*/ } #side-nav ul { @@ -621,32 +254,30 @@ ul.multicol { } pre.fragment { - border: 1px solid var(--fragment-border-color); - background-color: var(--fragment-background-color); - color: var(--fragment-foreground-color); + border: 1px solid #C4CFE5; + background-color: #FBFCFD; padding: 4px 6px; margin: 4px 8px 4px 2px; overflow: auto; word-wrap: break-word; font-size: 9pt; line-height: 125%; - font-family: var(--font-family-monospace); + font-family: monospace, fixed; font-size: 105%; } div.fragment { - padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/ - margin: 4px 8px 4px 2px; - color: var(--fragment-foreground-color); - background-color: var(--fragment-background-color); - border: 1px solid var(--fragment-border-color); + padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/ + margin: 4px 8px 4px 2px; + background-color: #FBFCFD; + border: 1px solid #C4CFE5; } div.line { - font-family: var(--font-family-monospace); + font-family: monospace, fixed; font-size: 13px; min-height: 13px; - line-height: 1.2; + line-height: 1.0; text-wrap: unrestricted; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ @@ -675,28 +306,24 @@ div.line:after { } div.line.glow { - background-color: var(--glow-color); - box-shadow: 0 0 10px var(--glow-color); + background-color: cyan; + box-shadow: 0 0 10px cyan; } span.lineno { padding-right: 4px; - margin-right: 9px; text-align: right; - border-right: 2px solid var(--fragment-lineno-border-color); - color: var(--fragment-lineno-foreground-color); - background-color: var(--fragment-lineno-background-color); + border-right: 2px solid #0F0; + background-color: #E8E8E8; white-space: pre; } -span.lineno a, span.lineno a:visited { - color: var(--fragment-lineno-link-fg-color); - background-color: var(--fragment-lineno-link-bg-color); +span.lineno a { + background-color: #D8D8D8; } span.lineno a:hover { - color: var(--fragment-lineno-link-hover-fg-color); - background-color: var(--fragment-lineno-link-hover-bg-color); + background-color: #C8C8C8; } .lineno { @@ -708,6 +335,24 @@ span.lineno a:hover { user-select: none; } +div.ah, span.ah { + background-color: black; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000 110%); +} + div.classindex ul { list-style: none; padding-left: 0; @@ -729,7 +374,8 @@ div.groupText { } body { - color: var(--page-foreground-color); + background-color: white; + color: black; margin: 0; } @@ -739,15 +385,29 @@ div.contents { margin-right: 8px; } -p.formulaDsp { - text-align: center; +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; } -img.dark-mode-visible { - display: none; +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; } -img.light-mode-visible { - display: none; + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; } img.formulaDsp { @@ -777,74 +437,89 @@ address.footer { img.footer { border: 0px; vertical-align: middle; - width: var(--footer-logo-width); -} - -.compoundTemplParams { - color: var(--memdecl-template-color); - font-size: 80%; - line-height: 120%; } /* @group Code Colorization */ span.keyword { - color: var(--code-keyword-color); + color: #008000 } span.keywordtype { - color: var(--code-type-keyword-color); + color: #604020 } span.keywordflow { - color: var(--code-flow-keyword-color); + color: #e08000 } span.comment { - color: var(--code-comment-color); + color: #800000 } span.preprocessor { - color: var(--code-preprocessor-color); + color: #806020 } span.stringliteral { - color: var(--code-string-literal-color); + color: #002080 } span.charliteral { - color: var(--code-char-literal-color); -} - -span.xmlcdata { - color: var(--code-xml-cdata-color); + color: #008080 } span.vhdldigit { - color: var(--code-vhdl-digit-color); + color: #ff00ff } span.vhdlchar { - color: var(--code-vhdl-char-color); + color: #000000 } span.vhdlkeyword { - color: var(--code-vhdl-keyword-color); + color: #700070 } span.vhdllogic { - color: var(--code-vhdl-logic-color); + color: #ff0000 } blockquote { - background-color: var(--blockquote-background-color); - border-left: 2px solid var(--blockquote-border-color); + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; margin: 0 24px 0 4px; padding: 0 12px 0 16px; } +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; +} + /* @end */ +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + td.tiny { font-size: 75%; } @@ -852,19 +527,18 @@ td.tiny { .dirtab { padding: 4px; border-collapse: collapse; - border: 1px solid var(--table-cell-border-color); + border: 1px solid #A3B4D7; } th.dirtab { - background-color: var(--table-header-background-color); - color: var(--table-header-foreground-color); + background: #EBEFF6; font-weight: bold; } hr { height: 0px; border: none; - border-top: 1px solid var(--separator-color); + border-top: 1px solid #4A6AAA; } hr.footer { @@ -892,14 +566,14 @@ table.memberdecls { } .memberdecls td.glow, .fieldtable tr.glow { - background-color: var(--glow-color); - box-shadow: 0 0 15px var(--glow-color); + background-color: cyan; + box-shadow: 0 0 15px cyan; } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: var(--memdecl-background-color); + background-color: #F9FAFC; border: none; margin: 4px; padding: 1px 0 0 8px; @@ -907,11 +581,11 @@ table.memberdecls { .mdescLeft, .mdescRight { padding: 0px 8px 4px 8px; - color: var(--memdecl-foreground-color); + color: #555; } .memSeparator { - border-bottom: 1px solid var(--memdecl-separator-color); + border-bottom: 1px solid #DEE4F0; line-height: 1px; margin: 0px; padding: 0px; @@ -926,7 +600,7 @@ table.memberdecls { } .memTemplParams { - color: var(--memdecl-template-color); + color: #4665A2; white-space: nowrap; font-size: 80%; } @@ -939,15 +613,15 @@ table.memberdecls { .memtitle { padding: 8px; - border-top: 1px solid var(--memdef-border-color); - border-left: 1px solid var(--memdef-border-color); - border-right: 1px solid var(--memdef-border-color); + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; border-top-right-radius: 4px; border-top-left-radius: 4px; margin-bottom: -1px; - background-image: var(--memdef-title-gradient-image); + background-image: url('nav_f.png'); background-repeat: repeat-x; - background-color: var(--memdef-title-background-color); + background-color: #E2E8F2; line-height: 1.25; font-weight: 300; float:left; @@ -962,11 +636,20 @@ table.memberdecls { .memtemplate { font-size: 80%; - color: var(--memdef-template-color); + color: #4665A2; font-weight: normal; margin-left: 9px; } +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + .mempage { width: 100%; } @@ -985,7 +668,7 @@ table.memberdecls { } .memitem.glow { - box-shadow: 0 0 15px var(--glow-color); + box-shadow: 0 0 15px cyan; } .memname { @@ -998,32 +681,41 @@ table.memberdecls { } .memproto, dl.reflist dt { - border-top: 1px solid var(--memdef-border-color); - border-left: 1px solid var(--memdef-border-color); - border-right: 1px solid var(--memdef-border-color); + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; padding: 6px 0px 6px 0px; - color: var(--memdef-proto-text-color); + color: #253555; font-weight: bold; - text-shadow: var(--memdef-proto-text-shadow); - background-color: var(--memdef-proto-background-color); + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-color: #DFE5F1; + /* opera specific markup */ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); border-top-right-radius: 4px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 4px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 4px; + } .overload { - font-family: var(--font-family-monospace); + font-family: "courier new",courier,monospace; font-size: 65%; } .memdoc, dl.reflist dd { - border-bottom: 1px solid var(--memdef-border-color); - border-left: 1px solid var(--memdef-border-color); - border-right: 1px solid var(--memdef-border-color); + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; padding: 6px 10px 2px 10px; + background-color: #FBFCFD; border-top-width: 0; background-image:url('nav_g.png'); background-repeat:repeat-x; - background-color: var(--memdef-doc-background-color); + background-color: #FFFFFF; /* opera specific markup */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; @@ -1056,7 +748,7 @@ dl.reflist dd { } .paramname { - color: var(--memdef-param-name-color); + color: #602020; white-space: nowrap; } .paramname em { @@ -1069,20 +761,20 @@ dl.reflist dd { .params, .retval, .exception, .tparams { margin-left: 0px; padding-left: 0px; -} +} .params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { font-weight: bold; vertical-align: top; } - + .params .paramtype, .tparams .paramtype { font-style: italic; vertical-align: top; -} - +} + .params .paramdir, .tparams .paramdir { - font-family: var(--font-family-monospace); + font-family: "courier new",courier,monospace; vertical-align: top; } @@ -1106,13 +798,13 @@ span.mlabels { } span.mlabel { - background-color: var(--label-background-color); - border-top:1px solid var(--label-left-top-border-color); - border-left:1px solid var(--label-left-top-border-color); - border-right:1px solid var(--label-right-bottom-border-color); - border-bottom:1px solid var(--label-right-bottom-border-color); + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; text-shadow: none; - color: var(--label-foreground-color); + color: white; margin-right: 4px; padding: 2px 3px; border-radius: 3px; @@ -1129,8 +821,8 @@ span.mlabel { div.directory { margin: 10px 0px; - border-top: 1px solid var(--directory-separator-color); - border-bottom: 1px solid var(--directory-separator-color); + border-top: 1px solid #9CAFD4; + border-bottom: 1px solid #9CAFD4; width: 100%; } @@ -1166,14 +858,9 @@ div.directory { border-left: 1px solid rgba(0,0,0,0.05); } -.directory tr.odd { - padding-left: 6px; - background-color: var(--index-odd-item-bg-color); -} - .directory tr.even { padding-left: 6px; - background-color: var(--index-even-item-bg-color); + background-color: #F7F8FB; } .directory img { @@ -1191,11 +878,11 @@ div.directory { cursor: pointer; padding-left: 2px; padding-right: 2px; - color: var(--page-link-color); + color: #3D578C; } .arrow { - color: var(--nav-arrow-color); + color: #9CAFD4; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; @@ -1209,15 +896,14 @@ div.directory { } .icon { - font-family: var(--font-family-icon); - line-height: normal; + font-family: Arial, Helvetica; font-weight: bold; font-size: 12px; height: 14px; width: 16px; display: inline-block; - background-color: var(--icon-background-color); - color: var(--icon-foreground-color); + background-color: #728DC1; + color: white; text-align: center; border-radius: 4px; margin-left: 2px; @@ -1234,7 +920,8 @@ div.directory { width: 24px; height: 18px; margin-bottom: 4px; - background-image:var(--icon-folder-open-image); + background-image:url('folderopen.png'); + background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; @@ -1244,7 +931,8 @@ div.directory { width: 24px; height: 18px; margin-bottom: 4px; - background-image:var(--icon-folder-closed-image); + background-image:url('folderclosed.png'); + background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; @@ -1254,13 +942,17 @@ div.directory { width: 24px; height: 18px; margin-bottom: 4px; - background-image:var(--icon-doc-image); + background-image:url('doc.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } +table.directory { + font: 400 14px Roboto,sans-serif; +} + /* @end */ div.dynheader { @@ -1275,7 +967,7 @@ div.dynheader { address { font-style: normal; - color: var(--footer-foreground-color); + color: #2A3D61; } table.doxtable caption { @@ -1289,23 +981,28 @@ table.doxtable { } table.doxtable td, table.doxtable th { - border: 1px solid var(--table-cell-border-color); + border: 1px solid #2D4068; padding: 3px 7px 2px; } table.doxtable th { - background-color: var(--table-header-background-color); - color: var(--table-header-foreground-color); + background-color: #374F7F; + color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } table.fieldtable { + /*width: 100%;*/ margin-bottom: 10px; - border: 1px solid var(--memdef-border-color); + border: 1px solid #A8B8D9; border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } @@ -1315,8 +1012,8 @@ table.fieldtable { .fieldtable td.fieldtype, .fieldtable td.fieldname { white-space: nowrap; - border-right: 1px solid var(--memdef-border-color); - border-bottom: 1px solid var(--memdef-border-color); + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; vertical-align: top; } @@ -1325,13 +1022,14 @@ table.fieldtable { } .fieldtable td.fielddoc { - border-bottom: 1px solid var(--memdef-border-color); + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ } .fieldtable td.fielddoc p:first-child { margin-top: 0px; -} - +} + .fieldtable td.fielddoc p:last-child { margin-bottom: 2px; } @@ -1341,18 +1039,22 @@ table.fieldtable { } .fieldtable th { - background-image: var(--memdef-title-gradient-image); + background-image:url('nav_f.png'); background-repeat:repeat-x; - background-color: var(--memdef-title-background-color); + background-color: #E2E8F2; font-size: 90%; - color: var(--memdef-proto-text-color); + color: #253555; padding-bottom: 4px; padding-top: 5px; text-align:left; font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; - border-bottom: 1px solid var(--memdef-border-color); + border-bottom: 1px solid #A8B8D9; } @@ -1360,7 +1062,7 @@ table.fieldtable { top: 0px; left: 10px; height: 36px; - background-image: var(--nav-gradient-image); + background-image: url('tab_b.png'); z-index: 101; overflow: hidden; font-size: 13px; @@ -1369,13 +1071,13 @@ table.fieldtable { .navpath ul { font-size: 11px; - background-image: var(--nav-gradient-image); + background-image:url('tab_b.png'); background-repeat:repeat-x; background-position: 0 -5px; height:30px; line-height:30px; - color:var(--nav-text-normal-color); - border:solid 1px var(--nav-breadcrumb-border-color); + color:#8AA0CC; + border:solid 1px #C2CDE4; overflow:hidden; margin:0px; padding:0px; @@ -1387,10 +1089,10 @@ table.fieldtable { float:left; padding-left:10px; padding-right:15px; - background-image:var(--nav-breadcrumb-image); + background-image:url('bc_s.png'); background-repeat:no-repeat; background-position:right; - color: var(--nav-foreground-color); + color:#364D7C; } .navpath li.navelem a @@ -1399,16 +1101,15 @@ table.fieldtable { display:block; text-decoration: none; outline: none; - color: var(--nav-text-normal-color); - font-family: var(--font-family-nav); - text-shadow: var(--nav-text-normal-shadow); - text-decoration: none; + color: #283A5D; + font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; } .navpath li.navelem a:hover { - color: var(--nav-text-hover-color); - text-shadow: var(--nav-text-hover-shadow); + color:#6884BD; } .navpath li.footer @@ -1420,7 +1121,7 @@ table.fieldtable { background-image:none; background-repeat:no-repeat; background-position:right; - color: var(--footer-foreground-color); + color:#364D7C; font-size: 8pt; } @@ -1432,7 +1133,7 @@ div.summary padding-right: 5px; width: 50%; text-align: right; -} +} div.summary a { @@ -1447,7 +1148,7 @@ table.classindex margin-right: 3%; width: 94%; border: 0; - border-spacing: 0; + border-spacing: 0; padding: 0; } @@ -1465,11 +1166,11 @@ div.ingroups a div.header { - background-image: var(--header-gradient-image); + background-image:url('nav_h.png'); background-repeat:repeat-x; - background-color: var(--header-background-color); + background-color: #F9FAFC; margin: 0px; - border-bottom: 1px solid var(--header-separator-color); + border-bottom: 1px solid #C4CFE5; } div.headertitle @@ -1492,6 +1193,11 @@ dl.section { padding-left: 0px; } +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; +} + dl.note { margin-left: -7px; padding-left: 3px; @@ -1499,6 +1205,16 @@ dl.note { border-color: #D0C000; } +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; +} + dl.warning, dl.attention { margin-left: -7px; padding-left: 3px; @@ -1506,6 +1222,16 @@ dl.warning, dl.attention { border-color: #FF0000; } +dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; +} + dl.pre, dl.post, dl.invariant { margin-left: -7px; padding-left: 3px; @@ -1513,6 +1239,16 @@ dl.pre, dl.post, dl.invariant { border-color: #00D000; } +dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; +} + dl.deprecated { margin-left: -7px; padding-left: 3px; @@ -1520,6 +1256,16 @@ dl.deprecated { border-color: #505050; } +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; +} + dl.todo { margin-left: -7px; padding-left: 3px; @@ -1527,6 +1273,16 @@ dl.todo { border-color: #00C0E0; } +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; +} + dl.test { margin-left: -7px; padding-left: 3px; @@ -1534,6 +1290,16 @@ dl.test { border-color: #3030E0; } +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; +} + dl.bug { margin-left: -7px; padding-left: 3px; @@ -1541,16 +1307,21 @@ dl.bug { border-color: #C08050; } +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; +} + dl.section dd { margin-bottom: 6px; } -#projectrow -{ - height: 56px; -} - #projectlogo { text-align: center; @@ -1566,29 +1337,25 @@ dl.section dd { #projectalign { vertical-align: middle; - padding-left: 0.5em; } #projectname { - font-size: 200%; - font-family: var(--font-family-title); + font: 300% Tahoma, Arial,sans-serif; margin: 0px; padding: 2px 0px; } - + #projectbrief { - font-size: 90%; - font-family: var(--font-family-title); + font: 120% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #projectnumber { - font-size: 50%; - font-family: 50% var(--font-family-title); + font: 50% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } @@ -1598,8 +1365,7 @@ dl.section dd { padding: 0px; margin: 0px; width: 100%; - border-bottom: 1px solid var(--title-separator-color); - background-color: var(--title-background-color); + border-bottom: 1px solid #5373B4; } .image @@ -1632,12 +1398,17 @@ dl.section dd { font-weight: bold; } +div.zoom +{ + border: 1px solid #90A5CE; +} + dl.citelist { margin-bottom:50px; } dl.citelist dt { - color:var(--citation-label-color); + color:#334975; float:left; font-weight:bold; margin-right:10px; @@ -1653,8 +1424,8 @@ dl.citelist dd { div.toc { padding: 14px 25px; - background-color: var(--toc-background-color); - border: 1px solid var(--toc-border-color); + background-color: #F4F6FA; + border: 1px solid #D8DFEE; border-radius: 7px 7px 7px 7px; float: right; height: auto; @@ -1662,17 +1433,28 @@ div.toc { width: 200px; } +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; +} + div.toc li { - background: var(--toc-down-arrow-image) no-repeat scroll 0 5px transparent; - font: 10px/1.2 var(--font-family-toc); + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; margin-top: 5px; padding-left: 10px; padding-top: 2px; } +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; +} + div.toc h3 { - font: bold 12px/1.2 var(--font-family-toc); - color: var(--toc-header-color); + font: bold 12px/1.2 Arial,FreeSans,sans-serif; + color: #4665A2; border-bottom: 0 none; margin: 0; } @@ -1681,7 +1463,7 @@ div.toc ul { list-style: none outside none; border: medium none; padding: 0px; -} +} div.toc li.level1 { margin-left: 0px; @@ -1692,11 +1474,11 @@ div.toc li.level2 { } div.toc li.level3 { - margin-left: 15px; + margin-left: 30px; } div.toc li.level4 { - margin-left: 15px; + margin-left: 45px; } span.emoji { @@ -1705,13 +1487,29 @@ span.emoji { */ } -span.obfuscator { - display: none; +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; +} + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; +} + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; +} + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; } .inherit_header { font-weight: bold; - color: var(--inherit-header-color); + color: gray; cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; @@ -1743,12 +1541,11 @@ tr.heading h2 { #powerTip { cursor: default; - /*white-space: nowrap;*/ - color: var(--tooltip-foreground-color); - background-color: var(--tooltip-background-color); - border: 1px solid var(--tooltip-border-color); + white-space: nowrap; + background-color: white; + border: 1px solid gray; border-radius: 4px 4px 4px 4px; - box-shadow: var(--tooltip-shadow); + box-shadow: 1px 1px 7px gray; display: none; font-size: smaller; max-width: 80%; @@ -1759,7 +1556,7 @@ tr.heading h2 { } #powerTip div.ttdoc { - color: var(--tooltip-doc-color); + color: grey; font-style: italic; } @@ -1767,24 +1564,18 @@ tr.heading h2 { font-weight: bold; } -#powerTip a { - color: var(--tooltip-link-color); -} - #powerTip div.ttname { font-weight: bold; } #powerTip div.ttdeci { - color: var(--tooltip-declaration-color); + color: #006318; } #powerTip div { margin: 0px; padding: 0px; - font-size: 12px; - font-family: var(--font-family-tooltip); - line-height: 16px; + font: 12px/16px Roboto,sans-serif; } #powerTip:before, #powerTip:after { @@ -1829,12 +1620,12 @@ tr.heading h2 { } #powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { - border-top-color: var(--tooltip-background-color); + border-top-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } -#powerTip.n:before, #powerTip.ne:before, #powerTip.nw:before { - border-top-color: var(--tooltip-border-color); +#powerTip.n:before { + border-top-color: #808080; border-width: 11px; margin: 0px -11px; } @@ -1857,13 +1648,13 @@ tr.heading h2 { } #powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { - border-bottom-color: var(--tooltip-background-color); + border-bottom-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } #powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { - border-bottom-color: var(--tooltip-border-color); + border-bottom-color: #808080; border-width: 11px; margin: 0px -11px; } @@ -1884,13 +1675,13 @@ tr.heading h2 { left: 100%; } #powerTip.e:after { - border-left-color: var(--tooltip-border-color); + border-left-color: #FFFFFF; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.e:before { - border-left-color: var(--tooltip-border-color); + border-left-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; @@ -1900,13 +1691,13 @@ tr.heading h2 { right: 100%; } #powerTip.w:after { - border-right-color: var(--tooltip-border-color); + border-right-color: #FFFFFF; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.w:before { - border-right-color: var(--tooltip-border-color); + border-right-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; @@ -1940,7 +1731,7 @@ table.markdownTable { } table.markdownTable td, table.markdownTable th { - border: 1px solid var(--table-cell-border-color); + border: 1px solid #2D4068; padding: 3px 7px 2px; } @@ -1948,8 +1739,8 @@ table.markdownTable tr { } th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { - background-color: var(--table-header-background-color); - color: var(--table-header-foreground-color); + background-color: #374F7F; + color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; @@ -1967,51 +1758,36 @@ th.markdownTableHeadCenter, td.markdownTableBodyCenter { text-align: center } -tt, code, kbd, samp -{ - display: inline-block; -} -/* @end */ - -u { - text-decoration: underline; -} - -details>summary { - list-style-type: none; +.DocNodeRTL { + text-align: right; + direction: rtl; } -details > summary::-webkit-details-marker { - display: none; +.DocNodeLTR { + text-align: left; + direction: ltr; } -details>summary::before { - content: "\25ba"; - padding-right:4px; - font-size: 80%; +table.DocNodeRTL { + width: auto; + margin-right: 0; + margin-left: auto; } -details[open]>summary::before { - content: "\25bc"; - padding-right:4px; - font-size: 80%; +table.DocNodeLTR { + width: auto; + margin-right: auto; + margin-left: 0; } -body { - scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-background-color); +tt, code, kbd, samp +{ + display: inline-block; + direction:ltr; } +/* @end */ -::-webkit-scrollbar { - background-color: var(--scrollbar-background-color); - height: 12px; - width: 12px; -} -::-webkit-scrollbar-thumb { - border-radius: 6px; - box-shadow: inset 0 0 12px 12px var(--scrollbar-thumb-color); - border: solid 2px transparent; -} -::-webkit-scrollbar-corner { - background-color: var(--scrollbar-background-color); +u { + text-decoration: underline; } diff --git a/doc/html/doxygen.svg b/doc/html/doxygen.svg index 79a76354..d42dad52 100644 --- a/doc/html/doxygen.svg +++ b/doc/html/doxygen.svg @@ -1,6 +1,4 @@ - @@ -19,7 +17,7 @@ - + diff --git a/doc/html/dynsections.js b/doc/html/dynsections.js index 1f4cd14a..88f2c27e 100644 --- a/doc/html/dynsections.js +++ b/doc/html/dynsections.js @@ -47,8 +47,6 @@ function updateStripes() { $('table.directory tr'). removeClass('even').filter(':visible:even').addClass('even'); - $('table.directory tr'). - removeClass('odd').filter(':visible:odd').addClass('odd'); } function toggleLevel(level) @@ -121,10 +119,10 @@ function toggleInherit(id) } } /* @license-end */ + $(document).ready(function() { $('.code,.codeRef').each(function() { $(this).data('powertip',$('#a'+$(this).attr('href').replace(/.*\//,'').replace(/[^a-z_A-Z0-9]/g,'_')).html()); - $.fn.powerTip.smartPlacementLists.s = [ 's', 'n', 'ne', 'se' ]; $(this).powerTip({ placement: 's', smartPlacement: true, mouseOnToPopup: true }); }); }); diff --git a/doc/html/files.html b/doc/html/files.html index a1469393..2a230904 100644 --- a/doc/html/files.html +++ b/doc/html/files.html @@ -1,9 +1,9 @@ - + - - + + qLibc: File List @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,51 +52,53 @@
-
File List
+
+
File List
Here is a list of all documented files with brief descriptions:
[detail level 123]
- + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
  containers
 qgrow.cGrow container that handles growable objects
 qgrow.cGrow container that handles growable objects
 qhasharr.cStatic(array) hash-table implementation
 qhashtbl.cHash-table container implementation
 qhashtbl.cHash-table container implementation
 qlist.cDoubly Linked-list implementation
 qlisttbl.cLinked-list-table implementation
 qlisttbl.cLinked-list-table implementation
 qqueue.cQueue implementation
 qstack.cStack implementation
 qstack.cStack implementation
 qtreetbl.cTree Table container that implements the Left-Leaning Red-Black BST algorithm
 qvector.cVector container implementation
 qvector.cVector container implementation
  extensions
 qaconf.cApache-style configuration file parser
 qaconf.cApache-style configuration file parser
 qconfig.cINI-style configuration file parser
 qdatabase.cDatabase wrapper
 qhttpclient.cHTTP client object
 qlog.cRotating file logger object
 qtokenbucket.cToken Bucket implementation
  include
  qlibc
 qlibc.hQlibc header file
 qlibcext.hQlibc extension header file
  ipc
 qsem.cSemaphore APIs
 qshm.cShared-memory APIs
  utilities
 qcount.cCounter file handling APIs
 qencode.cEncoding/decoding APIs
 qfile.cFile handling APIs
 qhash.cHash APIs
 qio.cI/O handling APIs
 qsocket.cSocket dandling APIs
 qstring.cString APIs
 qsystem.cSystem APIs
 qtime.cTime handling APIs
 qdatabase_mysql.cDatabase wrapper
 qdatabase_pgsql.cDatabase wrapper
 qhttpclient.cHTTP client object
 qlog.cRotating file logger object
 qtokenbucket.cToken Bucket implementation
  include
  qlibc
 qlibc.hQlibc header file
 qlibcext.hQlibc extension header file
  ipc
 qsem.cSemaphore APIs
 qshm.cShared-memory APIs
  utilities
 qcount.cCounter file handling APIs
 qencode.cEncoding/decoding APIs
 qfile.cFile handling APIs
 qhash.cHash APIs
 qio.cI/O handling APIs
 qsocket.cSocket dandling APIs
 qstring.cString APIs
 qsystem.cSystem APIs
 qtime.cTime handling APIs
@@ -105,7 +106,7 @@ diff --git a/doc/html/folderclosed.png b/doc/html/folderclosed.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8ab35edce8e97554e360005ee9fc5bffb36e66 GIT binary patch literal 616 zcmV-u0+;=XP)a9#ETzayK)T~Jw&MMH>OIr#&;dC}is*2Mqdf&akCc=O@`qC+4i z5Iu3w#1M@KqXCz8TIZd1wli&kkl2HVcAiZ8PUn5z_kG@-y;?yK06=cA0U%H0PH+kU zl6dp}OR(|r8-RG+YLu`zbI}5TlOU6ToR41{9=uz^?dGTNL;wIMf|V3`d1Wj3y!#6` zBLZ?xpKR~^2x}?~zA(_NUu3IaDB$tKma*XUdOZN~c=dLt_h_k!dbxm_*ibDM zlFX`g{k$X}yIe%$N)cn1LNu=q9_CS)*>A zsX_mM4L@`(cSNQKMFc$RtYbx{79#j-J7hk*>*+ZZhM4Hw?I?rsXCi#mRWJ=-0LGV5a-WR0Qgt<|Nqf)C-@80`5gIz45^_20000 - - - - - - - - - diff --git a/doc/html/folderclosedd.svg b/doc/html/folderclosedd.svg deleted file mode 100644 index 52f0166a..00000000 --- a/doc/html/folderclosedd.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/doc/html/folderopen.png b/doc/html/folderopen.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c7f676a3b3ef8c2c307d319dff3c6a604eb227 GIT binary patch literal 597 zcmV-b0;>IqP)X=#(TiCT&PiIIVc55T}TU}EUh*{q$|`3@{d>{Tc9Bo>e= zfmF3!f>fbI9#GoEHh0f`i5)wkLpva0ztf%HpZneK?w-7AK@b4Itw{y|Zd3k!fH?q2 zlhckHd_V2M_X7+)U&_Xcfvtw60l;--DgZmLSw-Y?S>)zIqMyJ1#FwLU*%bl38ok+! zh78H87n`ZTS;uhzAR$M`zZ`bVhq=+%u9^$5jDplgxd44}9;IRqUH1YHH|@6oFe%z( zo4)_>E$F&^P-f(#)>(TrnbE>Pefs9~@iN=|)Rz|V`sGfHNrJ)0gJb8xx+SBmRf@1l zvuzt=vGfI)<-F9!o&3l?>9~0QbUDT(wFdnQPv%xdD)m*g%!20>Bc9iYmGAp<9YAa( z0QgYgTWqf1qN++Gqp z8@AYPTB3E|6s=WLG?xw0tm|U!o=&zd+H0oRYE;Dbx+Na9s^STqX|Gnq%H8s(nGDGJ j8vwW|`Ts`)fSK|Kx=IK@RG@g200000NkvXXu0mjfauFEA literal 0 HcmV?d00001 diff --git a/doc/html/folderopen.svg b/doc/html/folderopen.svg deleted file mode 100644 index f6896dd2..00000000 --- a/doc/html/folderopen.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/doc/html/folderopend.svg b/doc/html/folderopend.svg deleted file mode 100644 index 2d1f06e7..00000000 --- a/doc/html/folderopend.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/doc/html/globals.html b/doc/html/globals.html index 1af063a9..ad1b5935 100644 --- a/doc/html/globals.html +++ b/doc/html/globals.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,21 +60,40 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- _ -

diff --git a/doc/html/globals_a.html b/doc/html/globals_a.html index 2d551f24..49d1d513 100644 --- a/doc/html/globals_a.html +++ b/doc/html/globals_a.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,15 +60,17 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- a -

diff --git a/doc/html/globals_b.html b/doc/html/globals_b.html index 78727745..2a668611 100644 --- a/doc/html/globals_b.html +++ b/doc/html/globals_b.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,15 +60,18 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- b -

diff --git a/doc/html/globals_c.html b/doc/html/globals_c.html index e106e441..98b8a6a1 100644 --- a/doc/html/globals_c.html +++ b/doc/html/globals_c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,17 +60,25 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- c -

diff --git a/doc/html/globals_d.html b/doc/html/globals_d.html index 9518d48b..ab0dc527 100644 --- a/doc/html/globals_d.html +++ b/doc/html/globals_d.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,16 +60,20 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- d -

diff --git a/doc/html/globals_defs.html b/doc/html/globals_defs.html index 74d2bdee..094ff639 100644 --- a/doc/html/globals_defs.html +++ b/doc/html/globals_defs.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,21 +52,23 @@
-
Here is a list of all documented macros with links to the documentation:
diff --git a/doc/html/globals_e.html b/doc/html/globals_e.html index aa73351d..050cd406 100644 --- a/doc/html/globals_e.html +++ b/doc/html/globals_e.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,19 +60,33 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- e -

diff --git a/doc/html/globals_f.html b/doc/html/globals_f.html index 16dcba4a..6a4dcf86 100644 --- a/doc/html/globals_f.html +++ b/doc/html/globals_f.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,16 +60,23 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- f -

diff --git a/doc/html/globals_func.html b/doc/html/globals_func.html index d8b9c397..5e43b573 100644 --- a/doc/html/globals_func.html +++ b/doc/html/globals_func.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,29 +52,48 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- _ -

diff --git a/doc/html/globals_func_a.html b/doc/html/globals_func_a.html index e699454e..a9246349 100644 --- a/doc/html/globals_func_a.html +++ b/doc/html/globals_func_a.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,23 +52,25 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- a -

diff --git a/doc/html/globals_func_b.html b/doc/html/globals_func_b.html index cc92ff48..d263fb06 100644 --- a/doc/html/globals_func_b.html +++ b/doc/html/globals_func_b.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,23 +52,26 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- b -

diff --git a/doc/html/globals_func_c.html b/doc/html/globals_func_c.html index 60beeeaf..55875af0 100644 --- a/doc/html/globals_func_c.html +++ b/doc/html/globals_func_c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,25 +52,33 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- c -

diff --git a/doc/html/globals_func_d.html b/doc/html/globals_func_d.html index 109110f5..f590b5b8 100644 --- a/doc/html/globals_func_d.html +++ b/doc/html/globals_func_d.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,23 +52,25 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- d -

diff --git a/doc/html/globals_func_e.html b/doc/html/globals_func_e.html index 481b30a7..56b72631 100644 --- a/doc/html/globals_func_e.html +++ b/doc/html/globals_func_e.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,27 +52,41 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- e -

diff --git a/doc/html/globals_func_f.html b/doc/html/globals_func_f.html index 789e126a..06fc8716 100644 --- a/doc/html/globals_func_f.html +++ b/doc/html/globals_func_f.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,24 +52,31 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- f -

diff --git a/doc/html/globals_func_g.html b/doc/html/globals_func_g.html index eedbd1b2..0d2f18ff 100644 --- a/doc/html/globals_func_g.html +++ b/doc/html/globals_func_g.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,26 +52,36 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- g -

diff --git a/doc/html/globals_func_h.html b/doc/html/globals_func_h.html index 4fd20379..e0d6bace 100644 --- a/doc/html/globals_func_h.html +++ b/doc/html/globals_func_h.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,23 +52,25 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- h -

diff --git a/doc/html/globals_func_n.html b/doc/html/globals_func_n.html index 634283ac..ffc89c80 100644 --- a/doc/html/globals_func_n.html +++ b/doc/html/globals_func_n.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,26 +52,34 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- n -

diff --git a/doc/html/globals_func_o.html b/doc/html/globals_func_o.html index c4854aa2..55ccd0b8 100644 --- a/doc/html/globals_func_o.html +++ b/doc/html/globals_func_o.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,23 +52,27 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- o -

diff --git a/doc/html/globals_func_p.html b/doc/html/globals_func_p.html index 8b73d23c..1419f4c7 100644 --- a/doc/html/globals_func_p.html +++ b/doc/html/globals_func_p.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,25 +52,32 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- p -

diff --git a/doc/html/globals_func_q.html b/doc/html/globals_func_q.html index 122665dd..0aed8973 100644 --- a/doc/html/globals_func_q.html +++ b/doc/html/globals_func_q.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,290 +52,829 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- q -

diff --git a/doc/html/globals_func_r.html b/doc/html/globals_func_r.html index e9c4934f..7ea4efae 100644 --- a/doc/html/globals_func_r.html +++ b/doc/html/globals_func_r.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,31 +52,54 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- r -

diff --git a/doc/html/globals_func_s.html b/doc/html/globals_func_s.html index 9b18f7e6..a44df52c 100644 --- a/doc/html/globals_func_s.html +++ b/doc/html/globals_func_s.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,31 +52,50 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- s -

diff --git a/doc/html/globals_func_w.html b/doc/html/globals_func_w.html index 6da86200..4a597d9f 100644 --- a/doc/html/globals_func_w.html +++ b/doc/html/globals_func_w.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,24 +52,29 @@
-
Here is a list of all documented functions with links to the documentation:
+  -

- w -

diff --git a/doc/html/globals_g.html b/doc/html/globals_g.html index be1021f8..fb6e1929 100644 --- a/doc/html/globals_g.html +++ b/doc/html/globals_g.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,18 +60,28 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- g -

diff --git a/doc/html/globals_h.html b/doc/html/globals_h.html index 10772eec..a95163c0 100644 --- a/doc/html/globals_h.html +++ b/doc/html/globals_h.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,15 +60,17 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- h -

diff --git a/doc/html/globals_n.html b/doc/html/globals_n.html index d8f334f3..451eb011 100644 --- a/doc/html/globals_n.html +++ b/doc/html/globals_n.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,18 +60,26 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- n -

diff --git a/doc/html/globals_o.html b/doc/html/globals_o.html index 00932dda..3c3884a8 100644 --- a/doc/html/globals_o.html +++ b/doc/html/globals_o.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,15 +60,19 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- o -

diff --git a/doc/html/globals_p.html b/doc/html/globals_p.html index ec7ff2f3..05db1c1e 100644 --- a/doc/html/globals_p.html +++ b/doc/html/globals_p.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,17 +60,24 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- p -

diff --git a/doc/html/globals_q.html b/doc/html/globals_q.html index d0a1f5be..90e1f4d2 100644 --- a/doc/html/globals_q.html +++ b/doc/html/globals_q.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,282 +60,821 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- q -

diff --git a/doc/html/globals_r.html b/doc/html/globals_r.html index 34b6db07..3b5b4da4 100644 --- a/doc/html/globals_r.html +++ b/doc/html/globals_r.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,23 +60,46 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- r -

diff --git a/doc/html/globals_s.html b/doc/html/globals_s.html index f3223bb2..29bedacc 100644 --- a/doc/html/globals_s.html +++ b/doc/html/globals_s.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,23 +60,42 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- s -

diff --git a/doc/html/globals_w.html b/doc/html/globals_w.html index bb2799fa..79f91469 100644 --- a/doc/html/globals_w.html +++ b/doc/html/globals_w.html @@ -1,9 +1,9 @@ - + - - + + qLibc: Globals @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,7 +52,7 @@
@@ -61,16 +60,21 @@
Here is a list of all documented functions, variables, defines, enums, and typedefs with links to the documentation:
-

- w -

diff --git a/doc/html/index.html b/doc/html/index.html index 965b1380..3c205f41 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: What's qLibc? <a href="https://github.com/wolkykim/qlibc/actions" ><img src="https://github.com/wolkykim/qlibc/workflows/CI/badge.svg" alt="Actions Status"/></a> +qLibc: What's qLibc? <a href="https://github.com/wolkykim/qlibc/actions"><img src="https://github.com/wolkykim/qlibc/workflows/CI/badge.svg" alt="Actions Status"/></a> @@ -20,8 +20,8 @@
- - + @@ -30,16 +30,15 @@
+
qLibc
- + +/* @license-end */
@@ -53,16 +52,17 @@
-
-
What's qLibc? Actions Status
+
+
+
What's qLibc? Actions Status
-

+

qLibc is currently one of the most functionally-complete, publicly-licensed C/C++ libraries. The goal of the qLibc project is to provide a simple and powerful general purpose C/C++ library that includes all kinds of containers and general library routines. It provides a ready-made set of common container APIs with a consistent API look.

qLibc Copyright

@@ -102,7 +102,7 @@

  • INI-style Configuration File Parser.
  • HTTP client.
  • Rotating File Logger.
  • -
  • Database(MySQL) interface.
  • +
  • Database(MySQL/PostgreSQL) interface.
  • Token-Bucket
  • @@ -149,7 +149,7 @@

    So, regardless of which container you use, you can simply put elements into a list with container->put(container, ...) or you can call them using direct API like qtreetbl_pub(container, ...).

    An examples below illustrates how it looks like.

    // create a hash-table.
    -
    qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    +
    qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    // add an element which key name is "score".
    int x = 12345;
    @@ -164,10 +164,10 @@

    // release table
    tbl->free(tbl);
    -
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition qhashtbl.c:127
    +
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition: qhashtbl.c:127

    Here is an identical implementation with a Linked-List-Table container. You may notice that there aren't any code changes at all, except for 1 line in the table creation. This is why qLibc encapsulates corresponding function pointers inside of the container object.

    // create a linked-list-table. THE ONLY LINE YOU NEED TO CHANGE.
    -
    qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    +
    qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    // add an element which key name is "score".
    int x = 12345;
    @@ -182,7 +182,7 @@

    // release table
    tbl->free(tbl);
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150

    Looking for people to work with.

    We're looking for people who want to work together to develop and improve qLibc. Currently, we have high demands on following areas.

    @@ -214,6 +214,7 @@

  • RQ
  • Ryan Gonzalez
  • Stroh Snow
  • +
  • SunBeau
  • Umesh
  • If we have forgotten or misspelled your name, please let us know.

    @@ -223,7 +224,7 @@

    diff --git a/doc/html/index.js b/doc/html/index.js deleted file mode 100644 index 18930935..00000000 --- a/doc/html/index.js +++ /dev/null @@ -1,9 +0,0 @@ -var index = -[ - [ "qLibc Copyright", "index.html#autotoc_md0", null ], - [ "API Reference", "index.html#autotoc_md1", null ], - [ "qLibc Tables at a Glance", "index.html#autotoc_md2", null ], - [ "Consistent API Look", "index.html#autotoc_md3", null ], - [ "Looking for people to work with.", "index.html#autotoc_md4", null ], - [ "Contributors", "index.html#autotoc_md5", null ] -]; \ No newline at end of file diff --git a/doc/html/jquery.js b/doc/html/jquery.js index 1dffb65b..103c32d7 100644 --- a/doc/html/jquery.js +++ b/doc/html/jquery.js @@ -1,11 +1,12 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=y(e||this.defaultElement||this)[0],this.element=y(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=y(),this.hoverable=y(),this.focusable=y(),this.classesElementLookup={},e!==this&&(y.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=y(e.style?e.ownerDocument:e.document||e),this.window=y(this.document[0].defaultView||this.document[0].parentWindow)),this.options=y.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:y.noop,_create:y.noop,_init:y.noop,destroy:function(){var i=this;this._destroy(),y.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:y.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return y.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t]=y.widget.extend({},this.options[t]),n=0;n

    "),i=e.children()[0];return y("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.widthx(D(s),D(n))?o.important="horizontal":o.important="vertical",p.using.call(this,t,o)}),h.offset(y.extend(l,{using:t}))})},y.ui.position={fit:{left:function(t,e){var i=e.within,s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,h=s-o,a=o+e.collisionWidth-n-s;e.collisionWidth>n?0n?0=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),y.ui.plugin={add:function(t,e,i){var s,n=y.ui[t].prototype;for(s in i)n.plugins[s]=n.plugins[s]||[],n.plugins[s].push([e,i[s]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;n
    ").css({overflow:"hidden",position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,t={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(t),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(t),this._proportionallyResize()),this._setupHandles(),e.autoHide&&y(this.element).on("mouseenter",function(){e.disabled||(i._removeClass("ui-resizable-autohide"),i._handles.show())}).on("mouseleave",function(){e.disabled||i.resizing||(i._addClass("ui-resizable-autohide"),i._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy(),this._addedHandles.remove();function t(t){y(t).removeData("resizable").removeData("ui-resizable").off(".resizable")}var e;return this.elementIsWrapper&&(t(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;case"aspectRatio":this._aspectRatio=!!e}},_setupHandles:function(){var t,e,i,s,n,o=this.options,h=this;if(this.handles=o.handles||(y(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=y(),this._addedHandles=y(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),i=this.handles.split(","),this.handles={},e=0;e"),this._addClass(n,"ui-resizable-handle "+s),n.css({zIndex:o.zIndex}),this.handles[t]=".ui-resizable-"+t,this.element.children(this.handles[t]).length||(this.element.append(n),this._addedHandles=this._addedHandles.add(n));this._renderAxis=function(t){var e,i,s;for(e in t=t||this.element,this.handles)this.handles[e].constructor===String?this.handles[e]=this.element.children(this.handles[e]).first().show():(this.handles[e].jquery||this.handles[e].nodeType)&&(this.handles[e]=y(this.handles[e]),this._on(this.handles[e],{mousedown:h._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(i=y(this.handles[e],this.element),s=/sw|ne|nw|se|n|s/.test(e)?i.outerHeight():i.outerWidth(),i=["padding",/ne|nw|n/.test(e)?"Top":/se|sw|s/.test(e)?"Bottom":/^e$/.test(e)?"Right":"Left"].join(""),t.css(i,s),this._proportionallyResize()),this._handles=this._handles.add(this.handles[e])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){h.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),h.axis=n&&n[1]?n[1]:"se")}),o.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._addedHandles.remove()},_mouseCapture:function(t){var e,i,s=!1;for(e in this.handles)(i=y(this.handles[e])[0])!==t.target&&!y.contains(i,t.target)||(s=!0);return!this.options.disabled&&s},_mouseStart:function(t){var e,i,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),e=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(e+=y(s.containment).scrollLeft()||0,i+=y(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:e,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalPosition={left:e,top:i},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,s=y(".ui-resizable-"+this.axis).css("cursor"),y("body").css("cursor","auto"===s?this.axis+"-resize":s),this._addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var e=this.originalMousePosition,i=this.axis,s=t.pageX-e.left||0,e=t.pageY-e.top||0,i=this._change[i];return this._updatePrevProperties(),i&&(e=i.apply(this,[t,s,e]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(e=this._updateRatio(e,t)),e=this._respectSize(e,t),this._updateCache(e),this._propagate("resize",t),e=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),y.isEmptyObject(e)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges())),!1},_mouseStop:function(t){this.resizing=!1;var e,i,s,n=this.options,o=this;return this._helper&&(s=(e=(i=this._proportionallyResizeElements).length&&/textarea/i.test(i[0].nodeName))&&this._hasScroll(i[0],"left")?0:o.sizeDiff.height,i=e?0:o.sizeDiff.width,e={width:o.helper.width()-i,height:o.helper.height()-s},i=parseFloat(o.element.css("left"))+(o.position.left-o.originalPosition.left)||null,s=parseFloat(o.element.css("top"))+(o.position.top-o.originalPosition.top)||null,n.animate||this.element.css(y.extend(e,{top:s,left:i})),o.helper.height(o.size.height),o.helper.width(o.size.width),this._helper&&!n.animate&&this._proportionallyResize()),y("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s=this.options,n={minWidth:this._isNumber(s.minWidth)?s.minWidth:0,maxWidth:this._isNumber(s.maxWidth)?s.maxWidth:1/0,minHeight:this._isNumber(s.minHeight)?s.minHeight:0,maxHeight:this._isNumber(s.maxHeight)?s.maxHeight:1/0};(this._aspectRatio||t)&&(e=n.minHeight*this.aspectRatio,i=n.minWidth/this.aspectRatio,s=n.maxHeight*this.aspectRatio,t=n.maxWidth/this.aspectRatio,e>n.minWidth&&(n.minWidth=e),i>n.minHeight&&(n.minHeight=i),st.width,h=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,a=this.originalPosition.left+this.originalSize.width,r=this.originalPosition.top+this.originalSize.height,l=/sw|nw|w/.test(i),i=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),h&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=a-e.minWidth),s&&l&&(t.left=a-e.maxWidth),h&&i&&(t.top=r-e.minHeight),n&&i&&(t.top=r-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];e<4;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;e
    ").css({overflow:"hidden"}),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++e.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize;return{left:this.originalPosition.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize;return{top:this.originalPosition.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},sw:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,e,i]))},ne:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},nw:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,e,i]))}},_propagate:function(t,e){y.ui.plugin.call(this,t,[e,this.ui()]),"resize"!==t&&this._trigger(t,e,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),y.ui.plugin.add("resizable","animate",{stop:function(e){var i=y(this).resizable("instance"),t=i.options,s=i._proportionallyResizeElements,n=s.length&&/textarea/i.test(s[0].nodeName),o=n&&i._hasScroll(s[0],"left")?0:i.sizeDiff.height,h=n?0:i.sizeDiff.width,n={width:i.size.width-h,height:i.size.height-o},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,o=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(y.extend(n,o&&h?{top:o,left:h}:{}),{duration:t.animateDuration,easing:t.animateEasing,step:function(){var t={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};s&&s.length&&y(s[0]).css({width:t.width,height:t.height}),i._updateCache(t),i._propagate("resize",e)}})}}),y.ui.plugin.add("resizable","containment",{start:function(){var i,s,n=y(this).resizable("instance"),t=n.options,e=n.element,o=t.containment,h=o instanceof y?o.get(0):/parent/.test(o)?e.parent().get(0):o;h&&(n.containerElement=y(h),/document/.test(o)||o===document?(n.containerOffset={left:0,top:0},n.containerPosition={left:0,top:0},n.parentData={element:y(document),left:0,top:0,width:y(document).width(),height:y(document).height()||document.body.parentNode.scrollHeight}):(i=y(h),s=[],y(["Top","Right","Left","Bottom"]).each(function(t,e){s[t]=n._num(i.css("padding"+e))}),n.containerOffset=i.offset(),n.containerPosition=i.position(),n.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},t=n.containerOffset,e=n.containerSize.height,o=n.containerSize.width,o=n._hasScroll(h,"left")?h.scrollWidth:o,e=n._hasScroll(h)?h.scrollHeight:e,n.parentData={element:h,left:t.left,top:t.top,width:o,height:e}))},resize:function(t){var e=y(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.position,o=e._aspectRatio||t.shiftKey,h={top:0,left:0},a=e.containerElement,t=!0;a[0]!==document&&/static/.test(a.css("position"))&&(h=s),n.left<(e._helper?s.left:0)&&(e.size.width=e.size.width+(e._helper?e.position.left-s.left:e.position.left-h.left),o&&(e.size.height=e.size.width/e.aspectRatio,t=!1),e.position.left=i.helper?s.left:0),n.top<(e._helper?s.top:0)&&(e.size.height=e.size.height+(e._helper?e.position.top-s.top:e.position.top),o&&(e.size.width=e.size.height*e.aspectRatio,t=!1),e.position.top=e._helper?s.top:0),i=e.containerElement.get(0)===e.element.parent().get(0),n=/relative|absolute/.test(e.containerElement.css("position")),i&&n?(e.offset.left=e.parentData.left+e.position.left,e.offset.top=e.parentData.top+e.position.top):(e.offset.left=e.element.offset().left,e.offset.top=e.element.offset().top),n=Math.abs(e.sizeDiff.width+(e._helper?e.offset.left-h.left:e.offset.left-s.left)),s=Math.abs(e.sizeDiff.height+(e._helper?e.offset.top-h.top:e.offset.top-s.top)),n+e.size.width>=e.parentData.width&&(e.size.width=e.parentData.width-n,o&&(e.size.height=e.size.width/e.aspectRatio,t=!1)),s+e.size.height>=e.parentData.height&&(e.size.height=e.parentData.height-s,o&&(e.size.width=e.size.height*e.aspectRatio,t=!1)),t||(e.position.left=e.prevPosition.left,e.position.top=e.prevPosition.top,e.size.width=e.prevSize.width,e.size.height=e.prevSize.height)},stop:function(){var t=y(this).resizable("instance"),e=t.options,i=t.containerOffset,s=t.containerPosition,n=t.containerElement,o=y(t.helper),h=o.offset(),a=o.outerWidth()-t.sizeDiff.width,o=o.outerHeight()-t.sizeDiff.height;t._helper&&!e.animate&&/relative/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o}),t._helper&&!e.animate&&/static/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o})}}),y.ui.plugin.add("resizable","alsoResize",{start:function(){var t=y(this).resizable("instance").options;y(t.alsoResize).each(function(){var t=y(this);t.data("ui-resizable-alsoresize",{width:parseFloat(t.width()),height:parseFloat(t.height()),left:parseFloat(t.css("left")),top:parseFloat(t.css("top"))})})},resize:function(t,i){var e=y(this).resizable("instance"),s=e.options,n=e.originalSize,o=e.originalPosition,h={height:e.size.height-n.height||0,width:e.size.width-n.width||0,top:e.position.top-o.top||0,left:e.position.left-o.left||0};y(s.alsoResize).each(function(){var t=y(this),s=y(this).data("ui-resizable-alsoresize"),n={},e=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];y.each(e,function(t,e){var i=(s[e]||0)+(h[e]||0);i&&0<=i&&(n[e]=i||null)}),t.css(n)})},stop:function(){y(this).removeData("ui-resizable-alsoresize")}}),y.ui.plugin.add("resizable","ghost",{start:function(){var t=y(this).resizable("instance"),e=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}),t._addClass(t.ghost,"ui-resizable-ghost"),!1!==y.uiBackCompat&&"string"==typeof t.options.ghost&&t.ghost.addClass(this.options.ghost),t.ghost.appendTo(t.helper)},resize:function(){var t=y(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=y(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),y.ui.plugin.add("resizable","grid",{resize:function(){var t,e=y(this).resizable("instance"),i=e.options,s=e.size,n=e.originalSize,o=e.originalPosition,h=e.axis,a="number"==typeof i.grid?[i.grid,i.grid]:i.grid,r=a[0]||1,l=a[1]||1,u=Math.round((s.width-n.width)/r)*r,p=Math.round((s.height-n.height)/l)*l,d=n.width+u,c=n.height+p,f=i.maxWidth&&i.maxWidthd,s=i.minHeight&&i.minHeight>c;i.grid=a,m&&(d+=r),s&&(c+=l),f&&(d-=r),g&&(c-=l),/^(se|s|e)$/.test(h)?(e.size.width=d,e.size.height=c):/^(ne)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.top=o.top-p):/^(sw)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.left=o.left-u):((c-l<=0||d-r<=0)&&(t=e._getPaddingPlusBorderDimensions(this)),0a;a++)for(i in o[a])n=o[a][i],o[a].hasOwnProperty(i)&&void 0!==n&&(e[i]=t.isPlainObject(n)?t.isPlainObject(e[i])?t.widget.extend({},e[i],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,i){var n=i.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=s.call(arguments,1),h=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(h=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):h=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new i(o,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
    "),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};l>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),h.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-r-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-r-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,m=-2*e.offset[1];0>c?(s=t.top+p+f+m+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+m)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+m-h,(i>0||u>a(i))&&(t.top+=p+f+m))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,h,l=i.nodeName.toLowerCase();return"area"===l?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(l)?(r=!i.disabled,r&&(h=t(i).closest("fieldset")[0],h&&(r=!h.disabled))):r="a"===l?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var n=!1;t(document).on("mouseup",function(){n=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!n){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),n=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,n=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
    ").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
    "),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,h=this._change[o];return this._updatePrevProperties(),h?(i=h.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,h=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(a,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,h=this.originalPosition.top+this.originalSize.height,l=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=r-e.minWidth),s&&l&&(t.left=r-e.maxWidth),a&&c&&(t.top=h-e.minHeight),n&&c&&(t.top=h-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
    "),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element +},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-a},l=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,h=t(this).resizable("instance"),l=h.options,c=h.element,u=l.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(h.containerElement=t(d),/document/.test(u)||u===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=h._num(e.css("padding"+s))}),h.containerOffset=e.offset(),h.containerPosition=e.position(),h.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,o=h.containerSize.width,a=h._hasScroll(d,"left")?d.scrollWidth:o,r=h._hasScroll(d)?d.scrollHeight:n,h.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,h=a.containerOffset,l=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(a._helper?h.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-h.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?h.left:0),l.top<(a._helper?h.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-h.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?h.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-h.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-h.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),h=a.outerWidth()-e.sizeDiff.width,l=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,c=h[1]||1,u=Math.round((n.width-o.width)/l)*l,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=h,_&&(p+=l),v&&(f+=c),m&&(p-=l),g&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-l)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-l>0?(i.size.width=p,i.position.left=a.left-u):(p=l-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable});/** * Copyright (c) 2007 Ariel Flesler - aflesler ○ gmail • com | https://github.com/flesler * Licensed under MIT * @author Ariel Flesler diff --git a/doc/html/menu.js b/doc/html/menu.js index b0b26936..2fe2214f 100644 --- a/doc/html/menu.js +++ b/doc/html/menu.js @@ -28,15 +28,7 @@ function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { if ('children' in data) { result+='
      '; for (var i in data.children) { - var url; - var link; - link = data.children[i].url; - if (link.substring(0,1)=='^') { - url = link.substring(1); - } else { - url = relPath+link; - } - result+='
    • '+ + result+='
    • '+ data.children[i].text+''+ makeTree(data.children[i],relPath)+'
    • '; } @@ -44,92 +36,15 @@ function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { } return result; } - var searchBoxHtml; - if (searchEnabled) { - if (serverSide) { - searchBoxHtml='
      '+ - '
      '+ - '
       '+ - ''+ - '
      '+ - '
      '+ - '
      '+ - '
      '; - } else { - searchBoxHtml='
      '+ - ''+ - ' '+ - ''+ - ''+ - ''+ - ''+ - ''+ - '
      '; - } - } - $('#main-nav').before('
      '+ - ''+ - ''+ - '
      '); $('#main-nav').append(makeTree(menudata,relPath)); $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); - if (searchBoxHtml) { - $('#main-menu').append('
    • '); - } - var $mainMenuState = $('#main-menu-state'); - var prevWidth = 0; - if ($mainMenuState.length) { - function initResizableIfExists() { - if (typeof initResizable==='function') initResizable(); - } - // animate mobile menu - $mainMenuState.change(function(e) { - var $menu = $('#main-menu'); - var options = { duration: 250, step: initResizableIfExists }; - if (this.checked) { - options['complete'] = function() { $menu.css('display', 'block') }; - $menu.hide().slideDown(options); - } else { - options['complete'] = function() { $menu.css('display', 'none') }; - $menu.show().slideUp(options); - } - }); - // set default menu visibility - function resetState() { - var $menu = $('#main-menu'); - var $mainMenuState = $('#main-menu-state'); - var newWidth = $(window).outerWidth(); - if (newWidth!=prevWidth) { - if ($(window).outerWidth()<768) { - $mainMenuState.prop('checked',false); $menu.hide(); - $('#searchBoxPos1').html(searchBoxHtml); - $('#searchBoxPos2').hide(); - } else { - $menu.show(); - $('#searchBoxPos1').empty(); - $('#searchBoxPos2').html(searchBoxHtml); - $('#searchBoxPos2').show(); - } - if (typeof searchBox!=='undefined') { - searchBox.CloseResultsWindow(); - } - prevWidth = newWidth; - } + if (searchEnabled) { + if (serverSide) { + $('#main-menu').append('
    • '); + } else { + $('#main-menu').append('
    • '); } - $(window).ready(function() { resetState(); initResizableIfExists(); }); - $(window).resize(resetState); } $('#main-menu').smartmenus(); } diff --git a/doc/html/nav_fd.png b/doc/html/nav_fd.png deleted file mode 100644 index 032fbdd4c54f54fa9a2e6423b94ef4b2ebdfaceb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQU#tajv*C{Z|C~*H7f|XvG1G8 zt7aS*L7xwMeS}!z6R#{C5tIw-s~AJ==F^i}x3XyJseHR@yF& zerFf(Zf;Dd{+(0lDIROL@Sj-Ju2JQ8&-n%4%q?>|^bShc&lR?}7HeMo@BDl5N(aHY Uj$gdr1MOz;boFyt=akR{0D!zeaR2}S diff --git a/doc/html/nav_hd.png b/doc/html/nav_hd.png deleted file mode 100644 index de80f18ad6488b9990303f267a76fdc83f0ffd80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^j6lr8!2~3AUOE6t21`#D$B+ufw|9379#G(63FK{W z5s6W-eg#Jd_@e6*DPn)w;=|1H}Zvm9l6xXXB%>yL=NQU;mg M>FVdQ&MBb@0Bdt1Qvd(} diff --git a/doc/html/navtree.css b/doc/html/navtree.css index c8a7766a..33341a67 100644 --- a/doc/html/navtree.css +++ b/doc/html/navtree.css @@ -22,13 +22,8 @@ #nav-tree .selected { background-image: url('tab_a.png'); background-repeat:repeat-x; - color: var(--nav-text-active-color); - text-shadow: var(--nav-text-active-shadow); -} - -#nav-tree .selected .arrow { - color: var(--nav-arrow-selected-color); - text-shadow: none; + color: #fff; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); } #nav-tree img { @@ -48,7 +43,7 @@ #nav-tree .label { margin:0px; padding:0px; - font: 12px var(--font-family-nav); + font: 12px 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; } #nav-tree .label a { @@ -57,7 +52,7 @@ #nav-tree .selected a { text-decoration:none; - color:var(--nav-text-active-color); + color:#fff; } #nav-tree .children_ul { @@ -72,6 +67,7 @@ #nav-tree { padding: 0px 0px; + background-color: #FAFAFF; font-size:14px; overflow:auto; } @@ -90,8 +86,7 @@ display:block; position: absolute; left: 0px; - width: $width; - overflow : hidden; + width: 250px; } .ui-resizable .ui-resizable-handle { @@ -99,7 +94,7 @@ } .ui-resizable-e { - background-image:var(--nav-splitbar-image); + background-image:url("splitbar.png"); background-size:100%; background-repeat:repeat-y; background-attachment: scroll; @@ -122,8 +117,9 @@ } #nav-tree { + background-image:url('nav_h.png'); background-repeat:repeat-x; - background-color: var(--nav-background-color); + background-color: #F9FAFC; -webkit-overflow-scrolling : touch; /* iOS 5+ */ } diff --git a/doc/html/navtree.js b/doc/html/navtree.js index f23e9849..1e272d31 100644 --- a/doc/html/navtree.js +++ b/doc/html/navtree.js @@ -325,14 +325,11 @@ function selectAndHighlight(hash,n) $(n.itemDiv).addClass('selected'); $(n.itemDiv).attr('id','selected'); } - var topOffset=5; - if (typeof page_layout!=='undefined' && page_layout==1) { - topOffset+=$('#top').outerHeight(); - } if ($('#nav-tree-contents .item:first').hasClass('selected')) { - topOffset+=25; + $('#nav-sync').css('top','30px'); + } else { + $('#nav-sync').css('top','5px'); } - $('#nav-sync').css('top',topOffset+'px'); showRoot(); } @@ -545,12 +542,5 @@ function initNavTree(toroot,relpath) navTo(o,toroot,hashUrl(),relpath); } }) - - $("div.toc a[href]").click(function(e) { - e.preventDefault(); - var docContent = $('#doc-content'); - var aname = $(this).attr("href"); - gotoAnchor($(aname),aname,true); - }) } /* @license-end */ diff --git a/doc/html/navtreedata.js b/doc/html/navtreedata.js index 364a2f3e..1910ffb2 100644 --- a/doc/html/navtreedata.js +++ b/doc/html/navtreedata.js @@ -25,7 +25,14 @@ var NAVTREE = [ [ "qLibc", "index.html", [ - [ "What's qLibc? \"Actions", "index.html", "index" ], + [ "What's qLibc? \"Actions", "index.html", [ + [ "qLibc Copyright", "index.html#autotoc_md0", null ], + [ "API Reference", "index.html#autotoc_md1", null ], + [ "qLibc Tables at a Glance", "index.html#autotoc_md2", null ], + [ "Consistent API Look", "index.html#autotoc_md3", null ], + [ "Looking for people to work with.", "index.html#autotoc_md4", null ], + [ "Contributors", "index.html#autotoc_md5", null ] + ] ], [ "Files", "files.html", [ [ "File List", "files.html", "files_dup" ], [ "Globals", "globals.html", [ @@ -40,7 +47,7 @@ var NAVTREE = var NAVTREEINDEX = [ "dir_0b61c55142250c0cc363383afd8075a4.html", -"qlisttbl_8c.html#a41e4658b9680da54c0d7b97ae046999c" +"qhttpclient_8c.html#af152fd3b6a32a801dc70a03119b13154" ]; var SYNCONMSG = 'click to disable panel synchronisation'; diff --git a/doc/html/navtreeindex0.js b/doc/html/navtreeindex0.js index 6151fa25..d7c628bf 100644 --- a/doc/html/navtreeindex0.js +++ b/doc/html/navtreeindex0.js @@ -16,8 +16,8 @@ var NAVTREEINDEX0 = "globals_defs.html":[1,1,2], "globals_e.html":[1,1,0,5], "globals_f.html":[1,1,0,6], -"globals_func.html":[1,1,1,0], "globals_func.html":[1,1,1], +"globals_func.html":[1,1,1,0], "globals_func_a.html":[1,1,1,1], "globals_func_b.html":[1,1,1,2], "globals_func_c.html":[1,1,1,3], @@ -55,84 +55,113 @@ var NAVTREEINDEX0 = "qaconf_8c.html#a1ee9ebbb7719b530a788d2d01256b2cc":[1,0,1,0,4], "qaconf_8c.html#a440db32385534bf03f08de253737623b":[1,0,1,0,2], "qaconf_8c.html#a4b7d9693f31e326b134f03f8ee4bca09":[1,0,1,0,1], -"qaconf_8c.html#a8723c8602a9c7a93f6bae2bce7a03cc4":[1,0,1,0,0], +"qaconf_8c.html#a5c09256d455a4cf7443aca42bccdf0e0":[1,0,1,0,5], "qaconf_8c.html#ab68c8ab80781fbca3aeaf7e3d16a9cc2":[1,0,1,0,3], "qaconf_8c.html#abad17eff2cc816719fdba22f271b0aab":[1,0,1,0,6], "qaconf_8c.html#abfcdc4a8d1067527f4c2d99b29d0db39":[1,0,1,0,7], -"qaconf_8c.html#adb0ea6a3d4fb4d4734e098b42dfd0671":[1,0,1,0,5], +"qaconf_8c.html#ac28c668ae735bb483d2c61c59f54cae0":[1,0,1,0,0], "qaconf_8c_source.html":[1,0,1,0], "qconfig_8c.html":[1,0,1,1], -"qconfig_8c.html#afcd4cc94bda4c854e3d26e5e358d5507":[1,0,1,1,0], -"qconfig_8c.html#afe9f5bb5dd171682d5d27e51151eac93":[1,0,1,1,1], +"qconfig_8c.html#a935668a20d0b3f5b6b84b46e9262bc0b":[1,0,1,1,1], +"qconfig_8c.html#ab62078f483e0002c4cd7d8f993a674a1":[1,0,1,1,0], +"qconfig_8c.html#ae70b6b473d2fcb3f17bde60297007336":[1,0,1,1,2], "qconfig_8c_source.html":[1,0,1,1], "qcount_8c.html":[1,0,4,0], "qcount_8c.html#a75f48f5285204edd64b6ec20fc5dad3c":[1,0,4,0,1], "qcount_8c.html#ab55bb62b3e48c20841df25f9c3d933e9":[1,0,4,0,0], "qcount_8c.html#ad6c28da6fdf0ea2c69180eeb72994918":[1,0,4,0,2], "qcount_8c_source.html":[1,0,4,0], -"qdatabase_8c.html":[1,0,1,2], -"qdatabase_8c.html#a0598e067ed6b749c9df042cd7bc83f5c":[1,0,1,2,11], -"qdatabase_8c.html#a1cb9916c08e1bef730d2f650f8c82a93":[1,0,1,2,5], -"qdatabase_8c.html#a26f8b55348c57f467d64c002ab306ff6":[1,0,1,2,21], -"qdatabase_8c.html#a2f27adbcda036187a4714e5bcd3a247e":[1,0,1,2,7], -"qdatabase_8c.html#a3386b5894d03e2f48deecb0b7c0632ea":[1,0,1,2,8], -"qdatabase_8c.html#a375ef96e283f327b2c4b3e745c1214a3":[1,0,1,2,6], -"qdatabase_8c.html#a396a7328f7fb499f89c2652598185dc8":[1,0,1,2,22], -"qdatabase_8c.html#a42069ce53c9e45b7283e9e44a7e021d9":[1,0,1,2,3], -"qdatabase_8c.html#a4ab66a07fa82b706895e1b2c19fcd501":[1,0,1,2,23], -"qdatabase_8c.html#a4b0bd360d7253d390c5775dd2608caa1":[1,0,1,2,15], -"qdatabase_8c.html#a4ba4b70bed91492b511152692005ace2":[1,0,1,2,0], -"qdatabase_8c.html#a59dbd0f78d2490fd50b2254451103368":[1,0,1,2,16], -"qdatabase_8c.html#a5dc988c35746fe8a792702523abc66b3":[1,0,1,2,10], -"qdatabase_8c.html#a6b5beda54ba78647276bc180f65138ca":[1,0,1,2,18], -"qdatabase_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7":[1,0,1,2,9], -"qdatabase_8c.html#a89fc888110ad23e6358d155e2c9b6a3b":[1,0,1,2,2], -"qdatabase_8c.html#a95d88451db51492ae6122701ce56b94d":[1,0,1,2,20], -"qdatabase_8c.html#a998f1143e4b00ff847f120e34a0efc0d":[1,0,1,2,12], -"qdatabase_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa":[1,0,1,2,4], -"qdatabase_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1":[1,0,1,2,17], -"qdatabase_8c.html#ae90a2206952d27df4a58995628741134":[1,0,1,2,1], -"qdatabase_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09":[1,0,1,2,19], -"qdatabase_8c.html#afe5578f18ea12e68e96a80b91bd9be9d":[1,0,1,2,14], -"qdatabase_8c.html#afeaa9b2ed0cde83bc01cb2996f8e5467":[1,0,1,2,13], -"qdatabase_8c_source.html":[1,0,1,2], +"qdatabase__mysql_8c.html":[1,0,1,2], +"qdatabase__mysql_8c.html#a0598e067ed6b749c9df042cd7bc83f5c":[1,0,1,2,11], +"qdatabase__mysql_8c.html#a11f5b1c5de493c27e1d396f7593408a0":[1,0,1,2,13], +"qdatabase__mysql_8c.html#a26f8b55348c57f467d64c002ab306ff6":[1,0,1,2,21], +"qdatabase__mysql_8c.html#a2f27adbcda036187a4714e5bcd3a247e":[1,0,1,2,7], +"qdatabase__mysql_8c.html#a3386b5894d03e2f48deecb0b7c0632ea":[1,0,1,2,8], +"qdatabase__mysql_8c.html#a396a7328f7fb499f89c2652598185dc8":[1,0,1,2,22], +"qdatabase__mysql_8c.html#a3c32ab62afb92fb6a55cfaff7c92bf42":[1,0,1,2,15], +"qdatabase__mysql_8c.html#a42069ce53c9e45b7283e9e44a7e021d9":[1,0,1,2,3], +"qdatabase__mysql_8c.html#a4ab66a07fa82b706895e1b2c19fcd501":[1,0,1,2,23], +"qdatabase__mysql_8c.html#a5dc988c35746fe8a792702523abc66b3":[1,0,1,2,10], +"qdatabase__mysql_8c.html#a6b5beda54ba78647276bc180f65138ca":[1,0,1,2,18], +"qdatabase__mysql_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7":[1,0,1,2,9], +"qdatabase__mysql_8c.html#a6fde99c74f91137ac554244ac60344be":[1,0,1,2,6], +"qdatabase__mysql_8c.html#a73ad0b6255fb4ac3c2c6870c5dd4e350":[1,0,1,2,0], +"qdatabase__mysql_8c.html#a89fc888110ad23e6358d155e2c9b6a3b":[1,0,1,2,2], +"qdatabase__mysql_8c.html#a95d88451db51492ae6122701ce56b94d":[1,0,1,2,20], +"qdatabase__mysql_8c.html#a998f1143e4b00ff847f120e34a0efc0d":[1,0,1,2,12], +"qdatabase__mysql_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa":[1,0,1,2,4], +"qdatabase__mysql_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1":[1,0,1,2,17], +"qdatabase__mysql_8c.html#abe4e5330b68b3fc9ff94b086939dba82":[1,0,1,2,5], +"qdatabase__mysql_8c.html#ae3559441aaa0ae5f19965612dccca8d5":[1,0,1,2,16], +"qdatabase__mysql_8c.html#ae90a2206952d27df4a58995628741134":[1,0,1,2,1], +"qdatabase__mysql_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09":[1,0,1,2,19], +"qdatabase__mysql_8c.html#afe5578f18ea12e68e96a80b91bd9be9d":[1,0,1,2,14], +"qdatabase__mysql_8c_source.html":[1,0,1,2], +"qdatabase__pgsql_8c.html":[1,0,1,3], +"qdatabase__pgsql_8c.html#a0598e067ed6b749c9df042cd7bc83f5c":[1,0,1,3,12], +"qdatabase__pgsql_8c.html#a11f5b1c5de493c27e1d396f7593408a0":[1,0,1,3,14], +"qdatabase__pgsql_8c.html#a26f8b55348c57f467d64c002ab306ff6":[1,0,1,3,22], +"qdatabase__pgsql_8c.html#a2f27adbcda036187a4714e5bcd3a247e":[1,0,1,3,8], +"qdatabase__pgsql_8c.html#a3386b5894d03e2f48deecb0b7c0632ea":[1,0,1,3,9], +"qdatabase__pgsql_8c.html#a396a7328f7fb499f89c2652598185dc8":[1,0,1,3,23], +"qdatabase__pgsql_8c.html#a3c32ab62afb92fb6a55cfaff7c92bf42":[1,0,1,3,16], +"qdatabase__pgsql_8c.html#a42069ce53c9e45b7283e9e44a7e021d9":[1,0,1,3,4], +"qdatabase__pgsql_8c.html#a4ab66a07fa82b706895e1b2c19fcd501":[1,0,1,3,24], +"qdatabase__pgsql_8c.html#a5dc988c35746fe8a792702523abc66b3":[1,0,1,3,11], +"qdatabase__pgsql_8c.html#a6779bf4ee99e4970276be7a408fce3bc":[1,0,1,3,0], +"qdatabase__pgsql_8c.html#a6b5beda54ba78647276bc180f65138ca":[1,0,1,3,19], +"qdatabase__pgsql_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7":[1,0,1,3,10], +"qdatabase__pgsql_8c.html#a6fde99c74f91137ac554244ac60344be":[1,0,1,3,7], +"qdatabase__pgsql_8c.html#a89fc888110ad23e6358d155e2c9b6a3b":[1,0,1,3,3], +"qdatabase__pgsql_8c.html#a95d88451db51492ae6122701ce56b94d":[1,0,1,3,21], +"qdatabase__pgsql_8c.html#a998f1143e4b00ff847f120e34a0efc0d":[1,0,1,3,13], +"qdatabase__pgsql_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa":[1,0,1,3,5], +"qdatabase__pgsql_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1":[1,0,1,3,18], +"qdatabase__pgsql_8c.html#abe4e5330b68b3fc9ff94b086939dba82":[1,0,1,3,6], +"qdatabase__pgsql_8c.html#adc1ca8409ff2e5d1d688600dc2c3b54d":[1,0,1,3,1], +"qdatabase__pgsql_8c.html#ae3559441aaa0ae5f19965612dccca8d5":[1,0,1,3,17], +"qdatabase__pgsql_8c.html#ae90a2206952d27df4a58995628741134":[1,0,1,3,2], +"qdatabase__pgsql_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09":[1,0,1,3,20], +"qdatabase__pgsql_8c.html#afe5578f18ea12e68e96a80b91bd9be9d":[1,0,1,3,15], +"qdatabase__pgsql_8c_source.html":[1,0,1,3], "qencode_8c.html":[1,0,4,1], +"qencode_8c.html#a254eadb7e79e9c22042c0779feac9e93":[1,0,4,1,1], "qencode_8c.html#a5bb9ee36ce61ff6061c7d8a50cafdcbf":[1,0,4,1,4], -"qencode_8c.html#a711145c1cafb961bfb85d61f3e7761db":[1,0,4,1,1], -"qencode_8c.html#a761de56d9987f1e312e6cd0587ec104d":[1,0,4,1,3], -"qencode_8c.html#aaf5feec6e64a6f74b8916bde73a214f9":[1,0,4,1,0], "qencode_8c.html#abe7be5bbdf9ee3928bf84f4663d272e8":[1,0,4,1,6], +"qencode_8c.html#ac3a72331130b2c04d2ca3c33bf0846b3":[1,0,4,1,0], +"qencode_8c.html#acf074770f495d087f9bbb268a52debb9":[1,0,4,1,5], +"qencode_8c.html#ad245826b85045ed82d49ad33200ae38d":[1,0,4,1,3], "qencode_8c.html#adefa85504919e3790adff444d8604496":[1,0,4,1,2], -"qencode_8c.html#aff18a5ca486ff7af31425c4f012afd7a":[1,0,4,1,5], "qencode_8c_source.html":[1,0,4,1], "qfile_8c.html":[1,0,4,2], -"qfile_8c.html#a2e381f2cbdcbeeaee40dc754414cbe01":[1,0,4,2,3], -"qfile_8c.html#a3adcdafab36f7466a54538a3ae30a728":[1,0,4,2,12], -"qfile_8c.html#a5386ae607fac65eb68fac034a8d4e612":[1,0,4,2,8], -"qfile_8c.html#a9133efad385ea27ea68c6df8fb94f1c4":[1,0,4,2,4], -"qfile_8c.html#a964a6571f9178ce478d61fe7c4f1021e":[1,0,4,2,1], -"qfile_8c.html#a9b19d1e8419deecd9519759a5e37805a":[1,0,4,2,13], -"qfile_8c.html#aaef22ff4a8a80e6356583335cc34db64":[1,0,4,2,5], -"qfile_8c.html#ac4728af539fcbdc7cd00e88834cfce74":[1,0,4,2,10], -"qfile_8c.html#ae623f84683c9aa6f58ead2f16a8f63db":[1,0,4,2,6], -"qfile_8c.html#aeb6452731c1582ac80bf26f91ce494c4":[1,0,4,2,9], -"qfile_8c.html#aee13ab9a468771f9b4e60ae117d1f613":[1,0,4,2,0], -"qfile_8c.html#af22eeea721ad66346d7e19f82b28fdb0":[1,0,4,2,2], -"qfile_8c.html#af382e5541d166ecb6229dcac10a029ff":[1,0,4,2,7], -"qfile_8c.html#af4b95ff503ddf957bc1d0d1a139a4be2":[1,0,4,2,11], +"qfile_8c.html#a0321a4d7ef62cd24639626eb4ccaf751":[1,0,4,2,9], +"qfile_8c.html#a0d1cd1e907efc6272ae969e702c7eb22":[1,0,4,2,4], +"qfile_8c.html#a145c18b1c6a05c971129cf7d4247b921":[1,0,4,2,14], +"qfile_8c.html#a1f45c0b35c3a43acd632d79c6dcfea70":[1,0,4,2,13], +"qfile_8c.html#a5e813a1311d212940f55ab4686933d1f":[1,0,4,2,0], +"qfile_8c.html#a964a6571f9178ce478d61fe7c4f1021e":[1,0,4,2,2], +"qfile_8c.html#aaa2d98ebd5690feeac3539499bf40f2e":[1,0,4,2,5], +"qfile_8c.html#aaef22ff4a8a80e6356583335cc34db64":[1,0,4,2,6], +"qfile_8c.html#ae623f84683c9aa6f58ead2f16a8f63db":[1,0,4,2,7], +"qfile_8c.html#aed46fca5010cfdedca9da70f904210fa":[1,0,4,2,11], +"qfile_8c.html#aee13ab9a468771f9b4e60ae117d1f613":[1,0,4,2,1], +"qfile_8c.html#aef38e80b0559f31af14dbeabbd1ac7a8":[1,0,4,2,10], +"qfile_8c.html#af22eeea721ad66346d7e19f82b28fdb0":[1,0,4,2,3], +"qfile_8c.html#af382e5541d166ecb6229dcac10a029ff":[1,0,4,2,8], +"qfile_8c.html#af4b95ff503ddf957bc1d0d1a139a4be2":[1,0,4,2,12], "qfile_8c_source.html":[1,0,4,2], "qgrow_8c.html":[1,0,0,0], "qgrow_8c.html#a00f87daf5492d6a042fca6e310086560":[1,0,0,0,10], "qgrow_8c.html#a093d851a279a44373605d7c57d498d4c":[1,0,0,0,9], "qgrow_8c.html#a0e4759834172363db2e63799da00696b":[1,0,0,0,2], "qgrow_8c.html#a313f8b8da26b36f9558525efe34cda29":[1,0,0,0,1], +"qgrow_8c.html#a3a0165e2eba9d9098f2e448791e8aae9":[1,0,0,0,0], "qgrow_8c.html#a3e37ab3040be6523fc93a135aea45949":[1,0,0,0,4], -"qgrow_8c.html#a4970eee21ac920bc459941c15c566866":[1,0,0,0,0], "qgrow_8c.html#a6c52a8b3243100e3f2b7a97f47b6b992":[1,0,0,0,3], "qgrow_8c.html#a748f12b95d3e7f199e77a503070fe62b":[1,0,0,0,8], -"qgrow_8c.html#a74e599b92cd949f803bb17ab9fe1b914":[1,0,0,0,7], -"qgrow_8c.html#a8df097303be8f8d1408a17cd7ee7c106":[1,0,0,0,6], +"qgrow_8c.html#ab1a0aaa7efbdbb40bac079d6ad103a8e":[1,0,0,0,7], "qgrow_8c.html#aec52e4932ddb3e1f29b95540f2231ac7":[1,0,0,0,5], +"qgrow_8c.html#af8dcd5f0d8fce7d4c9d1727076823019":[1,0,0,0,6], "qgrow_8c_source.html":[1,0,0,0], "qhash_8c.html":[1,0,4,3], "qhash_8c.html#a3ed9a8cb5f08fa3e3c58776a62f3f54f":[1,0,4,3,0], @@ -143,23 +172,25 @@ var NAVTREEINDEX0 = "qhash_8c.html#af14399e691041012350c3a82f337970e":[1,0,4,3,1], "qhash_8c_source.html":[1,0,4,3], "qhasharr_8c.html":[1,0,0,1], -"qhasharr_8c.html#a0c0674e4d7ce39cf336067732ba8cb7b":[1,0,0,1,14], -"qhasharr_8c.html#a2a780affb950d666046cfb5e1f9c8153":[1,0,0,1,13], -"qhasharr_8c.html#a3bc56610cce2c4f7ee2373ae4a40143b":[1,0,0,1,5], -"qhasharr_8c.html#a54fa504cce007a18fa510be544307cab":[1,0,0,1,11], -"qhasharr_8c.html#a63f33bbeb824803c310e672f2ceb8c70":[1,0,0,1,6], -"qhasharr_8c.html#a6b95b3177af93236950694298df2b7e4":[1,0,0,1,15], -"qhasharr_8c.html#a6e43029429b7aa43a66d2b55ce3bac81":[1,0,0,1,2], -"qhasharr_8c.html#a6f5f9ed05bab0600c251949db75f76f1":[1,0,0,1,1], -"qhasharr_8c.html#a83012ec9fea85ca5770529997306334d":[1,0,0,1,0], -"qhasharr_8c.html#a875e900cd73f72bcb613842ddf5b9c3a":[1,0,0,1,7], -"qhasharr_8c.html#a88e2f56b1a87c1f9485f5343ec7998be":[1,0,0,1,12], -"qhasharr_8c.html#a9334082a845acb3edc1f62d337266acd":[1,0,0,1,8], -"qhasharr_8c.html#a9df103f6016cd3d3267cdf42893ba809":[1,0,0,1,4], -"qhasharr_8c.html#aa5cd6756ea7192366386e811c9da8cee":[1,0,0,1,16], -"qhasharr_8c.html#aac1f0f336ac79a12590a7f0993e09997":[1,0,0,1,9], -"qhasharr_8c.html#aeadc9cda7a3e29e81a1f11b914c34390":[1,0,0,1,3], -"qhasharr_8c.html#af57be2bc8caf66f6bf94cd21370ef44f":[1,0,0,1,10], +"qhasharr_8c.html#a0c0674e4d7ce39cf336067732ba8cb7b":[1,0,0,1,16], +"qhasharr_8c.html#a2a780affb950d666046cfb5e1f9c8153":[1,0,0,1,15], +"qhasharr_8c.html#a30a8198420daadc7290a1bf3f82468ac":[1,0,0,1,10], +"qhasharr_8c.html#a3bc56610cce2c4f7ee2373ae4a40143b":[1,0,0,1,7], +"qhasharr_8c.html#a54fa504cce007a18fa510be544307cab":[1,0,0,1,13], +"qhasharr_8c.html#a5cd5ed2467b044e52699c5b14fdda7c2":[1,0,0,1,8], +"qhasharr_8c.html#a6b95b3177af93236950694298df2b7e4":[1,0,0,1,17], +"qhasharr_8c.html#a6e43029429b7aa43a66d2b55ce3bac81":[1,0,0,1,4], +"qhasharr_8c.html#a83012ec9fea85ca5770529997306334d":[1,0,0,1,2], +"qhasharr_8c.html#a8434b123a29f0b6da9b430700ce2b728":[1,0,0,1,9], +"qhasharr_8c.html#a88e2f56b1a87c1f9485f5343ec7998be":[1,0,0,1,14], +"qhasharr_8c.html#a9df103f6016cd3d3267cdf42893ba809":[1,0,0,1,6], +"qhasharr_8c.html#aa0f33578b50024a4a239472c52eee18c":[1,0,0,1,3], +"qhasharr_8c.html#aa5cd6756ea7192366386e811c9da8cee":[1,0,0,1,18], +"qhasharr_8c.html#aac1f0f336ac79a12590a7f0993e09997":[1,0,0,1,11], +"qhasharr_8c.html#ab725ad48f24a45370685c02f0005a78d":[1,0,0,1,1], +"qhasharr_8c.html#aeadc9cda7a3e29e81a1f11b914c34390":[1,0,0,1,5], +"qhasharr_8c.html#af57be2bc8caf66f6bf94cd21370ef44f":[1,0,0,1,12], +"qhasharr_8c.html#af6ff8340308b20e6f2e5c2f024313c5d":[1,0,0,1,0], "qhasharr_8c_source.html":[1,0,0,1], "qhashtbl_8c.html":[1,0,0,2], "qhashtbl_8c.html#a0e16876f85af045651b4e14a98760ee6":[1,0,0,2,9], @@ -169,85 +200,54 @@ var NAVTREEINDEX0 = "qhashtbl_8c.html#a35de9472338a15c45c1e7ffa9935b701":[1,0,0,2,15], "qhashtbl_8c.html#a3a9cf947d23f63b5a86e4ab6f815c4fd":[1,0,0,2,0], "qhashtbl_8c.html#a4a71fdf75c0db2e15984195f55b5bc27":[1,0,0,2,16], +"qhashtbl_8c.html#a4ebd413a2ffc36b4a72692aba402c4fd":[1,0,0,2,6], "qhashtbl_8c.html#a5c30813d4a8dbf3dbb23c394d7799393":[1,0,0,2,10], "qhashtbl_8c.html#a78ff1327078f1aac4294cad1c4e1953c":[1,0,0,2,11], -"qhashtbl_8c.html#a7a2314dcb8381b4599e8050becad2268":[1,0,0,2,1], "qhashtbl_8c.html#a87be8b838dc11139075428254493e8b3":[1,0,0,2,14], "qhashtbl_8c.html#a8832c9892ab3a4057dcf72589f00685a":[1,0,0,2,3], "qhashtbl_8c.html#aa156cfc5dbc28dd5c082dbf0d354fd5f":[1,0,0,2,2], "qhashtbl_8c.html#abfea47ba4675f272f0ecaf5041987f8b":[1,0,0,2,4], -"qhashtbl_8c.html#acd67a76b916bb5cd4b0aae2d37b23c9a":[1,0,0,2,7], -"qhashtbl_8c.html#adb6639e5a035c6c82d558f75d094bb8a":[1,0,0,2,6], +"qhashtbl_8c.html#acce512d557a3c776d474bfc977f34045":[1,0,0,2,1], +"qhashtbl_8c.html#ad0b71ffa5b24fd9b14ddfede6e872859":[1,0,0,2,7], "qhashtbl_8c.html#ae6a51c2268d057d6de84c0e3b661ba60":[1,0,0,2,5], "qhashtbl_8c_source.html":[1,0,0,2], -"qhttpclient_8c.html":[1,0,1,3], -"qhttpclient_8c.html#a12e0c5c0a30dafae0ece655d6095a75c":[1,0,1,3,2], -"qhttpclient_8c.html#a1d31e08f7bc114b63cd3140f5f8dc259":[1,0,1,3,10], -"qhttpclient_8c.html#a1f90b08bc44bdf06a60dae398d7ee297":[1,0,1,3,9], -"qhttpclient_8c.html#a2036374892fed2d1eff6eee25dcbfd9b":[1,0,1,3,15], -"qhttpclient_8c.html#a5c8eb3bb63df37fe248d858715894870":[1,0,1,3,1], -"qhttpclient_8c.html#a5dd8f813968295a3cfba53c0acaee487":[1,0,1,3,13], -"qhttpclient_8c.html#a6154b9124c5a57bccfb2dc47c791e555":[1,0,1,3,17], -"qhttpclient_8c.html#a69254acf43767884d060506b96f5c274":[1,0,1,3,14], -"qhttpclient_8c.html#a7a0cda760109d998ee40b94b665ef0ca":[1,0,1,3,18], -"qhttpclient_8c.html#a7ee9c5a86a3f561a5dcfe201ff20c350":[1,0,1,3,3], -"qhttpclient_8c.html#a865df9ac070a17d0751e1083af770bb1":[1,0,1,3,11], -"qhttpclient_8c.html#aa5f4b63d794316d3cdeafee2023b48c9":[1,0,1,3,0], -"qhttpclient_8c.html#aaab09f1f3ab6486633b0ea1c0fd65cd2":[1,0,1,3,5], -"qhttpclient_8c.html#ac3c34055be115314ffe7f18c3d19eb54":[1,0,1,3,6], -"qhttpclient_8c.html#add20b33d4c87e18756f7023bbc38f22e":[1,0,1,3,16], -"qhttpclient_8c.html#ade555b391a0ff83b074eb16a5ba2192f":[1,0,1,3,12], -"qhttpclient_8c.html#ae9ac3bd43c11509c0cbd8fb284bf13eb":[1,0,1,3,8], -"qhttpclient_8c.html#af16a3eb0912e8b5d4896466632d9c843":[1,0,1,3,7], -"qhttpclient_8c.html#af6bffb8f3d215b7225da20a6832bf440":[1,0,1,3,4], -"qhttpclient_8c_source.html":[1,0,1,3], -"qio_8c.html":[1,0,4,4], -"qio_8c.html#a0645cf0a3bcfc79ee3829012243d46d8":[1,0,4,4,3], -"qio_8c.html#a2c00200ce1766b2b81dafd9b9f20f6d0":[1,0,4,4,6], -"qio_8c.html#a8a695a43ebb605d243e56ad48937862e":[1,0,4,4,0], -"qio_8c.html#aa34911362f1793ed1e744d3eb63500db":[1,0,4,4,2], -"qio_8c.html#aafd34a0ab9f916ded37b1634cac3ab6a":[1,0,4,4,4], -"qio_8c.html#ab7ce64cc8cd837de47c91d1bdcb17cf5":[1,0,4,4,5], -"qio_8c.html#ac905811f0daa394ffef7beab68585a55":[1,0,4,4,1], -"qio_8c.html#ad2b7b0e9eb6e217a36422de18570b4e3":[1,0,4,4,7], -"qio_8c_source.html":[1,0,4,4], -"qlibc_8h.html":[1,0,2,0,0], -"qlibc_8h_source.html":[1,0,2,0,0], -"qlibcext_8h.html":[1,0,2,0,1], -"qlibcext_8h_source.html":[1,0,2,0,1], -"qlist_8c.html":[1,0,0,3], -"qlist_8c.html#a0247388edd52ee3a59485f46e525f5c3":[1,0,0,3,6], -"qlist_8c.html#a0960d05e9a2bab88c75505e4286de10a":[1,0,0,3,14], -"qlist_8c.html#a09fa7687257b3ef85eceed06d358e883":[1,0,0,3,23], -"qlist_8c.html#a13901f1a22643910f8de93d95b3571d8":[1,0,0,3,20], -"qlist_8c.html#a18e9f1aaca165a95810f7889ff27088e":[1,0,0,3,17], -"qlist_8c.html#a2148cf9191e31eed686f9c3894ee7fc9":[1,0,0,3,12], -"qlist_8c.html#a29a98c9eb9a93b096ca15db02010059a":[1,0,0,3,4], -"qlist_8c.html#a2fd39ba9956df93d34e557905bb41656":[1,0,0,3,9], -"qlist_8c.html#a362b854c881d11e4ff063bf2415e1202":[1,0,0,3,2], -"qlist_8c.html#a44e5775a38a30246388b5999a7ad7596":[1,0,0,3,24], -"qlist_8c.html#a49359e2de05870c5a6b8c1e8445c6865":[1,0,0,3,21], -"qlist_8c.html#a50b6b9d3f2ffe8230041e38129b5be97":[1,0,0,3,7], -"qlist_8c.html#a5fd1656d6b3534341d7775b201b70f3d":[1,0,0,3,3], -"qlist_8c.html#a666371a40b3d4347e0a73add22e90e1a":[1,0,0,3,16], -"qlist_8c.html#a7bbafb0265dbbd962629fbc6beec9a9d":[1,0,0,3,11], -"qlist_8c.html#a7fd9cac72fbe05f5dabf4dbde43ffd2e":[1,0,0,3,15], -"qlist_8c.html#a811fd272bfa6c07a5b190c6a1321dd49":[1,0,0,3,10], -"qlist_8c.html#a81f8833460fec073d5b49fd87a3a76fa":[1,0,0,3,5], -"qlist_8c.html#a8e982d17fa5c0d2d8a2380b54aad7fbd":[1,0,0,3,18], -"qlist_8c.html#ab4703d7188e6d1e5ca73827fc1aa29ad":[1,0,0,3,22], -"qlist_8c.html#ac80dea337388cd5a5ec319ea3aeae739":[1,0,0,3,8], -"qlist_8c.html#ad269a073f8f138a89d91fffccf4878b2":[1,0,0,3,1], -"qlist_8c.html#ad9937048b43bf3ced8a61ee16c6e5702":[1,0,0,3,0], -"qlist_8c.html#aed6f2725e1649367602ad497ef9c86bd":[1,0,0,3,19], -"qlist_8c.html#af8fafa8e268392b76496f752d9e010c6":[1,0,0,3,13], -"qlist_8c_source.html":[1,0,0,3], -"qlisttbl_8c.html":[1,0,0,4], -"qlisttbl_8c.html#a060ae322424932b3cc2fda83a037e1e2":[1,0,0,4,8], -"qlisttbl_8c.html#a073065190797d183a08f0a5325a01a1e":[1,0,0,4,1], -"qlisttbl_8c.html#a0df1c9358a8712794a8a0030a1687f62":[1,0,0,4,0], -"qlisttbl_8c.html#a10ea86e362efe15c888a91444d48af73":[1,0,0,4,3], -"qlisttbl_8c.html#a1dcf1c93e52b6f63f8c8ba0031a45803":[1,0,0,4,14], -"qlisttbl_8c.html#a2bc37eee74430649a238b7c3dd62518b":[1,0,0,4,4], -"qlisttbl_8c.html#a3e0d4e684118fd2b6f65b0a71c2306b4":[1,0,0,4,9] +"qhttpclient_8c.html":[1,0,1,4], +"qhttpclient_8c.html#a0023f7e49348c8c52b2fb8b547202ed3":[1,0,1,4,13], +"qhttpclient_8c.html#a00a05677bf92a38abf5be28b5ce0d294":[1,0,1,4,16], +"qhttpclient_8c.html#a00c11c6c6bf42cf1fb483c273454cf45":[1,0,1,4,3], +"qhttpclient_8c.html#a12e0c5c0a30dafae0ece655d6095a75c":[1,0,1,4,24], +"qhttpclient_8c.html#a16c7e21f17349c2515fe0134f9b7ffd4":[1,0,1,4,8], +"qhttpclient_8c.html#a1d31e08f7bc114b63cd3140f5f8dc259":[1,0,1,4,32], +"qhttpclient_8c.html#a2036374892fed2d1eff6eee25dcbfd9b":[1,0,1,4,37], +"qhttpclient_8c.html#a20eb5cc8af538c827de0f7ed9811243c":[1,0,1,4,10], +"qhttpclient_8c.html#a225a5999b63c699c4492a8412b315452":[1,0,1,4,7], +"qhttpclient_8c.html#a337b3f9b61cacbcde92a255249e4aae5":[1,0,1,4,18], +"qhttpclient_8c.html#a33f32423ccbf3a2a61f56cc5aee18cd8":[1,0,1,4,6], +"qhttpclient_8c.html#a34f24b8ca491cffac52bdd24140cd923":[1,0,1,4,20], +"qhttpclient_8c.html#a463a0b7aed650158725b205bc1bd0de8":[1,0,1,4,5], +"qhttpclient_8c.html#a471de168f41ecf5e8d62a43fea28b5d7":[1,0,1,4,2], +"qhttpclient_8c.html#a5c8eb3bb63df37fe248d858715894870":[1,0,1,4,23], +"qhttpclient_8c.html#a5dd8f813968295a3cfba53c0acaee487":[1,0,1,4,35], +"qhttpclient_8c.html#a6154b9124c5a57bccfb2dc47c791e555":[1,0,1,4,39], +"qhttpclient_8c.html#a69254acf43767884d060506b96f5c274":[1,0,1,4,36], +"qhttpclient_8c.html#a6d47746cc8b934944ed4f05308b2da63":[1,0,1,4,14], +"qhttpclient_8c.html#a78c49465c70c576daa60e34a84a9e47c":[1,0,1,4,12], +"qhttpclient_8c.html#a7a0cda760109d998ee40b94b665ef0ca":[1,0,1,4,40], +"qhttpclient_8c.html#a7c8d72a7d4eda32ad9a6211958463e97":[1,0,1,4,11], +"qhttpclient_8c.html#a7e41c8a47383e2bdf4b34ce5510bc9b6":[1,0,1,4,4], +"qhttpclient_8c.html#a7ee9c5a86a3f561a5dcfe201ff20c350":[1,0,1,4,25], +"qhttpclient_8c.html#a865df9ac070a17d0751e1083af770bb1":[1,0,1,4,33], +"qhttpclient_8c.html#a8c266040b26660f0c96255cadd8ee087":[1,0,1,4,19], +"qhttpclient_8c.html#a93983c38a271da5bde55becc02135bdb":[1,0,1,4,21], +"qhttpclient_8c.html#a9e272d1190eb37ce4447a9021f68a0c4":[1,0,1,4,15], +"qhttpclient_8c.html#aaab09f1f3ab6486633b0ea1c0fd65cd2":[1,0,1,4,27], +"qhttpclient_8c.html#aba396af03e2875b5ddedee43ad3c4707":[1,0,1,4,17], +"qhttpclient_8c.html#ac3c34055be115314ffe7f18c3d19eb54":[1,0,1,4,28], +"qhttpclient_8c.html#ac68e0a1d868dad52967d757dab5220db":[1,0,1,4,9], +"qhttpclient_8c.html#acba5e1d5c9b1666945f7b0ca4da0785b":[1,0,1,4,22], +"qhttpclient_8c.html#ad02ab7af04409020264521a798de2a5a":[1,0,1,4,0], +"qhttpclient_8c.html#ad1f324698e12fefadfc2ec5d16ce6a5b":[1,0,1,4,1], +"qhttpclient_8c.html#add20b33d4c87e18756f7023bbc38f22e":[1,0,1,4,38], +"qhttpclient_8c.html#ade555b391a0ff83b074eb16a5ba2192f":[1,0,1,4,34], +"qhttpclient_8c.html#ae9ac3bd43c11509c0cbd8fb284bf13eb":[1,0,1,4,30] }; diff --git a/doc/html/navtreeindex1.js b/doc/html/navtreeindex1.js index 0c06d30d..b387932f 100644 --- a/doc/html/navtreeindex1.js +++ b/doc/html/navtreeindex1.js @@ -1,47 +1,101 @@ var NAVTREEINDEX1 = { +"qhttpclient_8c.html#af152fd3b6a32a801dc70a03119b13154":[1,0,1,4,31], +"qhttpclient_8c.html#af16a3eb0912e8b5d4896466632d9c843":[1,0,1,4,29], +"qhttpclient_8c.html#af6bffb8f3d215b7225da20a6832bf440":[1,0,1,4,26], +"qhttpclient_8c_source.html":[1,0,1,4], +"qio_8c.html":[1,0,4,4], +"qio_8c.html#a0645cf0a3bcfc79ee3829012243d46d8":[1,0,4,4,4], +"qio_8c.html#a2c00200ce1766b2b81dafd9b9f20f6d0":[1,0,4,4,7], +"qio_8c.html#a52246cf5f9d0bf7f359dfc0f92d555ed":[1,0,4,4,0], +"qio_8c.html#a8a695a43ebb605d243e56ad48937862e":[1,0,4,4,1], +"qio_8c.html#aa34911362f1793ed1e744d3eb63500db":[1,0,4,4,3], +"qio_8c.html#aafd34a0ab9f916ded37b1634cac3ab6a":[1,0,4,4,5], +"qio_8c.html#ab7ce64cc8cd837de47c91d1bdcb17cf5":[1,0,4,4,6], +"qio_8c.html#ac905811f0daa394ffef7beab68585a55":[1,0,4,4,2], +"qio_8c.html#ad2b7b0e9eb6e217a36422de18570b4e3":[1,0,4,4,8], +"qio_8c_source.html":[1,0,4,4], +"qlibc_8h.html":[1,0,2,0,0], +"qlibc_8h_source.html":[1,0,2,0,0], +"qlibcext_8h.html":[1,0,2,0,1], +"qlibcext_8h_source.html":[1,0,2,0,1], +"qlist_8c.html":[1,0,0,3], +"qlist_8c.html#a083e4eb3b82834756608e88a15a38164":[1,0,0,3,10], +"qlist_8c.html#a0960d05e9a2bab88c75505e4286de10a":[1,0,0,3,14], +"qlist_8c.html#a09fa7687257b3ef85eceed06d358e883":[1,0,0,3,23], +"qlist_8c.html#a171f1b505417b5a936e198993c9cb372":[1,0,0,3,0], +"qlist_8c.html#a18e9f1aaca165a95810f7889ff27088e":[1,0,0,3,17], +"qlist_8c.html#a19d80e0634bda2350372bc15f5b92970":[1,0,0,3,9], +"qlist_8c.html#a2148cf9191e31eed686f9c3894ee7fc9":[1,0,0,3,12], +"qlist_8c.html#a29a98c9eb9a93b096ca15db02010059a":[1,0,0,3,4], +"qlist_8c.html#a362b854c881d11e4ff063bf2415e1202":[1,0,0,3,2], +"qlist_8c.html#a44e5775a38a30246388b5999a7ad7596":[1,0,0,3,24], +"qlist_8c.html#a49359e2de05870c5a6b8c1e8445c6865":[1,0,0,3,21], +"qlist_8c.html#a4a27432727387f75f1118dcadb818bc5":[1,0,0,3,20], +"qlist_8c.html#a5fd1656d6b3534341d7775b201b70f3d":[1,0,0,3,3], +"qlist_8c.html#a666371a40b3d4347e0a73add22e90e1a":[1,0,0,3,16], +"qlist_8c.html#a7bbafb0265dbbd962629fbc6beec9a9d":[1,0,0,3,11], +"qlist_8c.html#a7fd9cac72fbe05f5dabf4dbde43ffd2e":[1,0,0,3,15], +"qlist_8c.html#a8aa5812e608102e152a4e6c906fd848c":[1,0,0,3,6], +"qlist_8c.html#a8e982d17fa5c0d2d8a2380b54aad7fbd":[1,0,0,3,18], +"qlist_8c.html#ab4703d7188e6d1e5ca73827fc1aa29ad":[1,0,0,3,22], +"qlist_8c.html#ab988e96c223ecdf63105f6797e42ecd9":[1,0,0,3,8], +"qlist_8c.html#acd6f76ee0d811b1c7f9be494bded46f3":[1,0,0,3,19], +"qlist_8c.html#ad269a073f8f138a89d91fffccf4878b2":[1,0,0,3,1], +"qlist_8c.html#ae5158e057b797633b43190806fc49ce8":[1,0,0,3,7], +"qlist_8c.html#ae591e0ee1380e484a7707beed87ada6f":[1,0,0,3,5], +"qlist_8c.html#af8fafa8e268392b76496f752d9e010c6":[1,0,0,3,13], +"qlist_8c_source.html":[1,0,0,3], +"qlisttbl_8c.html":[1,0,0,4], +"qlisttbl_8c.html#a073065190797d183a08f0a5325a01a1e":[1,0,0,4,1], +"qlisttbl_8c.html#a10ea86e362efe15c888a91444d48af73":[1,0,0,4,3], +"qlisttbl_8c.html#a1dcf1c93e52b6f63f8c8ba0031a45803":[1,0,0,4,14], +"qlisttbl_8c.html#a2bc37eee74430649a238b7c3dd62518b":[1,0,0,4,4], +"qlisttbl_8c.html#a3e0d4e684118fd2b6f65b0a71c2306b4":[1,0,0,4,9], "qlisttbl_8c.html#a41e4658b9680da54c0d7b97ae046999c":[1,0,0,4,11], "qlisttbl_8c.html#a47c3a686f2d4bbf628c39d4cb41b5969":[1,0,0,4,13], "qlisttbl_8c.html#a6dfcb2ffbd01fd4273605bc4684cccd9":[1,0,0,4,21], "qlisttbl_8c.html#a6eeeb3c4a73a9e6c80a18d2a8a0c0351":[1,0,0,4,19], "qlisttbl_8c.html#a6fa35afd89db427de7454a7dc174f366":[1,0,0,4,20], -"qlisttbl_8c.html#a759de18f2f11bfb03a7d07a1c06f3152":[1,0,0,4,6], +"qlisttbl_8c.html#a7990202b09ab853c07221458755278b6":[1,0,0,4,6], "qlisttbl_8c.html#a7b4fe528a8b64efd31e7927fbcfcc7ab":[1,0,0,4,2], "qlisttbl_8c.html#a90de20495b1a65ecb1cb6a94cced3a0f":[1,0,0,4,15], "qlisttbl_8c.html#a9c96de65d427e08ce4e3a34340fd7bdd":[1,0,0,4,16], "qlisttbl_8c.html#ab8e4a8e033d630b7c7a88ba608a60ab4":[1,0,0,4,18], "qlisttbl_8c.html#abda3a975b1c2869d4af44cb69674252b":[1,0,0,4,12], "qlisttbl_8c.html#ac494b02523019a449c29cf1049ef4e60":[1,0,0,4,10], -"qlisttbl_8c.html#ad5d3f27fe3d3e32327463dc86c73283e":[1,0,0,4,5], +"qlisttbl_8c.html#ace8778acce088344531b5249d580aca7":[1,0,0,4,0], "qlisttbl_8c.html#aee9cb892f83b75dd7d573c59ac83b3f5":[1,0,0,4,17], "qlisttbl_8c.html#af14aa8d272fa39b24260ead37410e4ac":[1,0,0,4,7], +"qlisttbl_8c.html#af5c61d4dd8b45def720b4e96708da7f2":[1,0,0,4,8], +"qlisttbl_8c.html#afb58625be8850adff352adeaccf3be83":[1,0,0,4,5], "qlisttbl_8c_source.html":[1,0,0,4], -"qlog_8c.html":[1,0,1,4], -"qlog_8c.html#a0f1c08e4e0616731b8c5be87a8752f72":[1,0,1,4,2], -"qlog_8c.html#a1d9678a766dbbe0cf4863296588a70ce":[1,0,1,4,1], -"qlog_8c.html#a3c66d4096cbaac34db4659e04a0d6335":[1,0,1,4,3], -"qlog_8c.html#a58ed43ff476576586b46c4f6744f1fd2":[1,0,1,4,5], -"qlog_8c.html#a9753872b3edbc89389d22688fdc0afc2":[1,0,1,4,4], -"qlog_8c.html#ad58c2d6cf7daa2976934150e69c83498":[1,0,1,4,0], -"qlog_8c_source.html":[1,0,1,4], +"qlog_8c.html":[1,0,1,5], +"qlog_8c.html#a0f1c08e4e0616731b8c5be87a8752f72":[1,0,1,5,2], +"qlog_8c.html#a1d9678a766dbbe0cf4863296588a70ce":[1,0,1,5,1], +"qlog_8c.html#a3c66d4096cbaac34db4659e04a0d6335":[1,0,1,5,3], +"qlog_8c.html#a58ed43ff476576586b46c4f6744f1fd2":[1,0,1,5,5], +"qlog_8c.html#a9753872b3edbc89389d22688fdc0afc2":[1,0,1,5,4], +"qlog_8c.html#acfc1a51cddb0a4e8555ea66f42276874":[1,0,1,5,0], +"qlog_8c_source.html":[1,0,1,5], "qqueue_8c.html":[1,0,0,5], "qqueue_8c.html#a00afe6738073f11568f5b75c11bd11a2":[1,0,0,5,11], -"qqueue_8c.html#a0a561f175ff24b26dea93b3540363e4d":[1,0,0,5,9], +"qqueue_8c.html#a08067efa9b39e73c2984f58a3d045906":[1,0,0,5,9], "qqueue_8c.html#a3111eeb954b1af9c0c8a67f96968b988":[1,0,0,5,2], "qqueue_8c.html#a382fb2a1977afc2b129bded75a41ed8b":[1,0,0,5,3], "qqueue_8c.html#a39066762f093f61a310dc3b3550c8780":[1,0,0,5,15], "qqueue_8c.html#a407c506258030896676079f9125132b9":[1,0,0,5,14], -"qqueue_8c.html#a6859700f743f55461f8ca56beba6437f":[1,0,0,5,5], -"qqueue_8c.html#a7250d2c5928e63093344e559f629fbbc":[1,0,0,5,6], +"qqueue_8c.html#a4e9a5e7b48086a5335de5047ab7da8f3":[1,0,0,5,8], "qqueue_8c.html#a732d5260a04c2f67fa28f690eb31566c":[1,0,0,5,13], "qqueue_8c.html#a748db13e1474f59abbc49f922cb90b76":[1,0,0,5,16], -"qqueue_8c.html#a984579020101ea2eec6f6e074148923b":[1,0,0,5,10], -"qqueue_8c.html#aa72722b6d5a7ecc5ec01f71c0d7aa951":[1,0,0,5,8], -"qqueue_8c.html#abd187f46c2a60edaa0b1bd82bb8a1426":[1,0,0,5,0], +"qqueue_8c.html#a7bd8537313542cb0db0c912fce4af056":[1,0,0,5,0], +"qqueue_8c.html#a86b2d6fab3d2426cda120d46e17ec65a":[1,0,0,5,5], +"qqueue_8c.html#a947c732d507e74898625d385c7595d00":[1,0,0,5,12], +"qqueue_8c.html#a95ad438031ccd21b02169b9b685851c3":[1,0,0,5,6], "qqueue_8c.html#abe2952d24a9a9f6cc8121f491faec4c4":[1,0,0,5,4], +"qqueue_8c.html#ac04a0dcde8dba0b43c1c8ee3aa87035e":[1,0,0,5,10], "qqueue_8c.html#ada41104b85c01a5773263c3ff84f9dc5":[1,0,0,5,1], "qqueue_8c.html#ade3de0f1655569417856bb9758f1017c":[1,0,0,5,7], -"qqueue_8c.html#ae22aa53bd411431a0809888655797848":[1,0,0,5,12], "qqueue_8c_source.html":[1,0,0,5], "qsem_8c.html":[1,0,3,0], "qsem_8c.html#a023df7733cebc81fc0aa0285772a914d":[1,0,3,0,4], @@ -54,7 +108,7 @@ var NAVTREEINDEX1 = "qsem_8c.html#acd893b0a1a6b390f27621ae0e93a5dd8":[1,0,3,0,2], "qsem_8c_source.html":[1,0,3,0], "qshm_8c.html":[1,0,3,1], -"qshm_8c.html#a04d0a8cb2c672f568931132d0c1b5246":[1,0,3,1,2], +"qshm_8c.html#a4ad7d06e37d47bb6c35a8c37b1c0a50e":[1,0,3,1,2], "qshm_8c.html#a881b11a2d26ae83230151955545630ed":[1,0,3,1,0], "qshm_8c.html#aa6b1f31987b42fe18dc6be25e58c0bce":[1,0,3,1,1], "qshm_8c.html#aec816f3889e85322e0304c3cbd67c639":[1,0,3,1,3], @@ -63,124 +117,130 @@ var NAVTREEINDEX1 = "qsocket_8c.html#a4285c6dd3c52560f3d471f332f6558bc":[1,0,4,5,2], "qsocket_8c.html#a536b1963c73ec5ace917a57ffed1f3cd":[1,0,4,5,1], "qsocket_8c.html#a86e043367b6ba401408c64736192319d":[1,0,4,5,0], -"qsocket_8c.html#aefe8cc8f14190e19e81d9ec090007bd2":[1,0,4,5,3], +"qsocket_8c.html#aac413225be88d873f0d6f19f2ee5e42b":[1,0,4,5,3], "qsocket_8c_source.html":[1,0,4,5], "qstack_8c.html":[1,0,0,6], -"qstack_8c.html#a1a467bd3711514b767b8093aa768ab1b":[1,0,0,6,8], "qstack_8c.html#a1ca1db3402919fe147e32a16ee56a051":[1,0,0,6,14], +"qstack_8c.html#a20cfe4a51fa37348293d199b22530b04":[1,0,0,6,8], "qstack_8c.html#a227a2c394e088afee835b79aa1e81a90":[1,0,0,6,4], "qstack_8c.html#a243157bbd832b1a29da906cdf1e067d1":[1,0,0,6,1], "qstack_8c.html#a2a091ec90ef14302cb29ea07c1ce84b2":[1,0,0,6,16], "qstack_8c.html#a355b4b772da518e3e63702e61df44886":[1,0,0,6,7], "qstack_8c.html#a3c626c12f0f8ad9b1899c4d6e9fa78a6":[1,0,0,6,3], "qstack_8c.html#a3eac4be39a2ae68c7290735b034fc66c":[1,0,0,6,13], -"qstack_8c.html#a3ed81d0e8555fb6167d4be53c3ddb9ae":[1,0,0,6,0], -"qstack_8c.html#a5f57044ab0d93ba510ad9ec44491d969":[1,0,0,6,5], -"qstack_8c.html#a66b564e1ea27a75ec3fa4e13b9b34eb4":[1,0,0,6,9], -"qstack_8c.html#a80484101311b715fbb867da8c3616c11":[1,0,0,6,10], -"qstack_8c.html#a9b0e45524a375b63cd1d07ee8b99665a":[1,0,0,6,12], -"qstack_8c.html#ace3dc06378d4cde45bede6c4032a7151":[1,0,0,6,6], +"qstack_8c.html#a513b235ab408d98c6324fbef841e3e1a":[1,0,0,6,12], +"qstack_8c.html#a5a4924e9e1201c9049d0baa68d4c1da7":[1,0,0,6,6], +"qstack_8c.html#a6e2940f626e655f803cb78c452780c1a":[1,0,0,6,10], +"qstack_8c.html#a79ecf0838fc92a6fde5e43fcac757426":[1,0,0,6,5], +"qstack_8c.html#aae38a11a6be7fbea79f3ca51c65a4aee":[1,0,0,6,9], +"qstack_8c.html#ad59200fd9723a575e042365b916e150b":[1,0,0,6,0], "qstack_8c.html#af37f84c47e9cd48b3f070fa96777029e":[1,0,0,6,11], "qstack_8c.html#af40fcb35c362981495c37aa7963ff50d":[1,0,0,6,15], "qstack_8c.html#af6bd78d22c5ac2a5dbcc4102416a1682":[1,0,0,6,2], "qstack_8c_source.html":[1,0,0,6], "qstring_8c.html":[1,0,4,6], -"qstring_8c.html#a019f74d6452ba69f93c1905a2c5363b2":[1,0,4,6,12], -"qstring_8c.html#a052c99b86a780692b68ba6cda90b477d":[1,0,4,6,15], -"qstring_8c.html#a24dcde4b707ed664b3962e1bf400de0f":[1,0,4,6,5], -"qstring_8c.html#a2e9c44c24a06d24bccc1967972c63909":[1,0,4,6,17], -"qstring_8c.html#a38e5f0c9670254df2a1e4246e13d0c54":[1,0,4,6,16], -"qstring_8c.html#a3be760da8b51532dad32d483bdbcf7b5":[1,0,4,6,6], -"qstring_8c.html#a673d4ee449a08223e9f26508fa7d81a3":[1,0,4,6,0], +"qstring_8c.html#a01772aae3cdd533ff6f0f68367df9226":[1,0,4,6,4], +"qstring_8c.html#a1a4b63ebdee0be75f217d52dc7cb1750":[1,0,4,6,2], +"qstring_8c.html#a4ab535e3b8608f1ef81ed21a7d602cef":[1,0,4,6,10], +"qstring_8c.html#a6183cbf7350c2373915feac84b5ec25c":[1,0,4,6,15], +"qstring_8c.html#a6ad659c9b3a4f6b515368cb0fa098d32":[1,0,4,6,8], "qstring_8c.html#a6ddf2449ee1edb1139005fa88213567c":[1,0,4,6,19], -"qstring_8c.html#a70a69740b083dfe3116b2dd025aebdcc":[1,0,4,6,1], -"qstring_8c.html#a728b578553bfb9fe6b88af5a26a95e2d":[1,0,4,6,10], -"qstring_8c.html#a82ebc5e528abf5257099994578904b51":[1,0,4,6,3], -"qstring_8c.html#a86f97f3fb3a432af6fa1dd55cdc100bc":[1,0,4,6,8], +"qstring_8c.html#a737037e2fc79cc1fa8de2a6fedaf21ed":[1,0,4,6,1], +"qstring_8c.html#a75b5521fd90e830770182d68d613fc43":[1,0,4,6,18], "qstring_8c.html#a87849863c09feb068f316ffb63aaef03":[1,0,4,6,21], -"qstring_8c.html#a90c6f8a88cb9de91d5e74fbd055d4551":[1,0,4,6,7], -"qstring_8c.html#aa27b0a87aa68e3ee318e3280a5f9d9d5":[1,0,4,6,9], -"qstring_8c.html#ab20a0316dfb2bc4f48c69d54fcb21355":[1,0,4,6,2], -"qstring_8c.html#abd9722d596de17775ff99107d124404c":[1,0,4,6,11], -"qstring_8c.html#ac4aec98006a9b0f9eb54c8c43121425e":[1,0,4,6,4], -"qstring_8c.html#acc14c95a2ddadcd8d983d6e12d372d12":[1,0,4,6,13], -"qstring_8c.html#ad42f6086a25ed4be54f2a18a8973dbd8":[1,0,4,6,14], -"qstring_8c.html#ae5c7f3c367f90928d56b2db1cd60f646":[1,0,4,6,18], -"qstring_8c.html#ae5ce5ba220fb8ad35c4434d54912f00b":[1,0,4,6,22], +"qstring_8c.html#aa0fc982fbf9c6163d98cbb46e6faa86d":[1,0,4,6,5], +"qstring_8c.html#aa694e3f14941ac016fccbb456e24a134":[1,0,4,6,13], +"qstring_8c.html#aaab287c88286f42b65eeb52af24a3aa8":[1,0,4,6,3], +"qstring_8c.html#abd1fa48bffd427c7c3847eebc9681f3b":[1,0,4,6,11], +"qstring_8c.html#acc32dbf9f419ef637d0547211b7125b9":[1,0,4,6,9], +"qstring_8c.html#acc9b4062910be63934d6b1658359b9bd":[1,0,4,6,12], +"qstring_8c.html#ad17ac23f4397125c8bdfd9359f2b6780":[1,0,4,6,14], +"qstring_8c.html#ad300f196f08a8243c384f2f59f06f67b":[1,0,4,6,17], +"qstring_8c.html#ae5317b2503cfc75f1b7faaef86da33d6":[1,0,4,6,22], +"qstring_8c.html#ae656735491546fa512358935c381e19a":[1,0,4,6,6], +"qstring_8c.html#aef51c584fd78c0430ccfb5f51c32cf71":[1,0,4,6,0], +"qstring_8c.html#af45ab0b307be585b9b24aa45e37ad5eb":[1,0,4,6,16], "qstring_8c.html#af8e066e1239a1670b9640bdf0e456b89":[1,0,4,6,20], +"qstring_8c.html#afed8e04d6f83692b815251e9f76cc8fd":[1,0,4,6,7], "qstring_8c_source.html":[1,0,4,6], "qsystem_8c.html":[1,0,4,7], -"qsystem_8c.html#a3913af5fdd1ab50b75601d112a965988":[1,0,4,7,0], -"qsystem_8c.html#a739f64aa9825fe23965b11d71b0674f5":[1,0,4,7,1], +"qsystem_8c.html#a5463b8e0d69c2f317da6d9c47dee14f4":[1,0,4,7,1], +"qsystem_8c.html#a572fee0d88280a0dd1f5ab0905296eea":[1,0,4,7,0], "qsystem_8c_source.html":[1,0,4,7], "qtime_8c.html":[1,0,4,8], -"qtime_8c.html#a11790ec49dee0ad865ca7392790a6b8b":[1,0,4,8,4], -"qtime_8c.html#a393447c2ed7764c85937ecdb3ed5a252":[1,0,4,8,5], -"qtime_8c.html#a6116a83810dfd2d593e236abb417bdcd":[1,0,4,8,2], -"qtime_8c.html#a689f608f871f45fb772266bf1f18cdd7":[1,0,4,8,0], -"qtime_8c.html#a6c536941cca900b390fc199eb3b50c6b":[1,0,4,8,7], -"qtime_8c.html#a7f023ccf5f6120df54e4f1a9f034b9a9":[1,0,4,8,1], -"qtime_8c.html#ac83d07a78536a71e3161c00156342677":[1,0,4,8,6], -"qtime_8c.html#afb4e26399a9e779429aa0592e4ad03b6":[1,0,4,8,3], +"qtime_8c.html#a0feed44026ab24d9d366c5997a2173b4":[1,0,4,8,4], +"qtime_8c.html#a1a3f24d676b909313177459cefa78899":[1,0,4,8,7], +"qtime_8c.html#a689f608f871f45fb772266bf1f18cdd7":[1,0,4,8,3], +"qtime_8c.html#a6c536941cca900b390fc199eb3b50c6b":[1,0,4,8,10], +"qtime_8c.html#a78c99ffd76a7bb3c8c74db76207e9ab4":[1,0,4,8,1], +"qtime_8c.html#a7e54e9b38d82df0fe112440a89b372dc":[1,0,4,8,9], +"qtime_8c.html#a8773045a81f883f2ab00761f45e8642c":[1,0,4,8,0], +"qtime_8c.html#a89b28885c2bb13906855cd6c34cfaed7":[1,0,4,8,5], +"qtime_8c.html#acbacded92c55a0674eec7590b65d7a3d":[1,0,4,8,8], +"qtime_8c.html#ad3d8a3bd0c0b677acef144f2c2ef6d73":[1,0,4,8,2], +"qtime_8c.html#adf628449c7e5425b5c16b61f14a51eaf":[1,0,4,8,6], "qtime_8c_source.html":[1,0,4,8], -"qtokenbucket_8c.html":[1,0,1,5], -"qtokenbucket_8c.html#a5464647c7e3874f8517fdbe787ee6ce3":[1,0,1,5,2], -"qtokenbucket_8c.html#a8da36d9c0f027f0a24086526593e2eab":[1,0,1,5,0], -"qtokenbucket_8c.html#aec09c4e8a1ef411e4a5ee6d9036dbca0":[1,0,1,5,1], -"qtokenbucket_8c_source.html":[1,0,1,5], +"qtokenbucket_8c.html":[1,0,1,6], +"qtokenbucket_8c.html#a5464647c7e3874f8517fdbe787ee6ce3":[1,0,1,6,2], +"qtokenbucket_8c.html#a8da36d9c0f027f0a24086526593e2eab":[1,0,1,6,0], +"qtokenbucket_8c.html#aec09c4e8a1ef411e4a5ee6d9036dbca0":[1,0,1,6,1], +"qtokenbucket_8c_source.html":[1,0,1,6], "qtreetbl_8c.html":[1,0,0,7], -"qtreetbl_8c.html#a0a5c039995463a94bb7066190a6653e1":[1,0,0,7,0], -"qtreetbl_8c.html#a106198f0800d8892e404e0020da0d685":[1,0,0,7,6], -"qtreetbl_8c.html#a27db959def75174ccdab3fd073e8b8fc":[1,0,0,7,21], +"qtreetbl_8c.html#a1f037a026ec877becd4f010226e00ea4":[1,0,0,7,12], +"qtreetbl_8c.html#a1f70611ea89775d0ec580cc13eaa0069":[1,0,0,7,13], +"qtreetbl_8c.html#a27db959def75174ccdab3fd073e8b8fc":[1,0,0,7,22], "qtreetbl_8c.html#a2bac240b706b85273338a26fb986eee7":[1,0,0,7,19], "qtreetbl_8c.html#a31e5908f6885033a58e354928e489b83":[1,0,0,7,3], "qtreetbl_8c.html#a3354408a36cb5d6fd724727dda04d1f3":[1,0,0,7,9], "qtreetbl_8c.html#a3537212a86616d50459eec229d94f8e4":[1,0,0,7,15], "qtreetbl_8c.html#a49e05111c8541c7155e352fb618cf170":[1,0,0,7,2], -"qtreetbl_8c.html#a590374e22090d003406210704b1c91d4":[1,0,0,7,13], +"qtreetbl_8c.html#a54b587a2c2d1a377cdc54c0e2e32d400":[1,0,0,7,7], +"qtreetbl_8c.html#a598c177098cb87a5d2f9e898cc2cfeed":[1,0,0,7,0], "qtreetbl_8c.html#a5c8aa458bac0dc5e7dc0ab97c6f52dfd":[1,0,0,7,17], -"qtreetbl_8c.html#a663dfe23addb8debd1c3da5e54d2c930":[1,0,0,7,20], +"qtreetbl_8c.html#a61a3463749adb906e549d51f8a9c5a51":[1,0,0,7,20], +"qtreetbl_8c.html#a663dfe23addb8debd1c3da5e54d2c930":[1,0,0,7,21], "qtreetbl_8c.html#a74b90269930ae1607271c187514ac5e5":[1,0,0,7,5], -"qtreetbl_8c.html#a756340d5313f0ecf604c35326b3b6a5f":[1,0,0,7,8], +"qtreetbl_8c.html#a7782fbe2dc1aa9f1be267c67e5ab3e37":[1,0,0,7,8], "qtreetbl_8c.html#a7b324cf9e317a2623c5767b8a0183f72":[1,0,0,7,16], -"qtreetbl_8c.html#a80185e2ca955ce2ccbdb43065110df4c":[1,0,0,7,7], "qtreetbl_8c.html#a88a8e67f478390c6b25780fda3134a8d":[1,0,0,7,18], -"qtreetbl_8c.html#a9d9d33c61cc0c7e3312c10504d628e98":[1,0,0,7,24], +"qtreetbl_8c.html#a9d9d33c61cc0c7e3312c10504d628e98":[1,0,0,7,25], "qtreetbl_8c.html#aa2c4f3ac4623feb251fadabbdf734bdd":[1,0,0,7,14], -"qtreetbl_8c.html#aa727bb7c8a149d2b924d586594836d86":[1,0,0,7,23], -"qtreetbl_8c.html#ab53aff8d2c853903ecd90ff83eba2056":[1,0,0,7,25], -"qtreetbl_8c.html#ac12b7b164b262053427eac34a2d1cbbc":[1,0,0,7,12], +"qtreetbl_8c.html#aa727bb7c8a149d2b924d586594836d86":[1,0,0,7,24], +"qtreetbl_8c.html#ab3232b711f0462533a066ecffb01a297":[1,0,0,7,6], +"qtreetbl_8c.html#ab53aff8d2c853903ecd90ff83eba2056":[1,0,0,7,26], "qtreetbl_8c.html#ac475383244c35814d0950f0fa0c6414e":[1,0,0,7,4], -"qtreetbl_8c.html#acca3f6925989955eec665c5c9aa11ed2":[1,0,0,7,22], +"qtreetbl_8c.html#acca3f6925989955eec665c5c9aa11ed2":[1,0,0,7,23], "qtreetbl_8c.html#ad3284afb03b64b8242687e33b87aaf0e":[1,0,0,7,10], "qtreetbl_8c.html#aeb32bf5a193a0b7276cff302d3fcd090":[1,0,0,7,11], "qtreetbl_8c.html#af9e239f384bd9939c34361a45dd496a6":[1,0,0,7,1], "qtreetbl_8c_source.html":[1,0,0,7], "qvector_8c.html":[1,0,0,8], -"qvector_8c.html#a0bf76991577f397aafecb52599709103":[1,0,0,8,1], -"qvector_8c.html#a0da63e7c76f7cf42db2e5e027bd51a02":[1,0,0,8,12], -"qvector_8c.html#a11d7f8b4c7ac35bbfb60b168ae2a5046":[1,0,0,8,23], -"qvector_8c.html#a131073e557942bf36271cb01a3ccaf43":[1,0,0,8,5], -"qvector_8c.html#a160e7f61e805c0b89e50ba6c2d74ba13":[1,0,0,8,10], -"qvector_8c.html#a217d5fe3cb59560703d84e47097dc989":[1,0,0,8,20], -"qvector_8c.html#a251c32fb5bafe5e1beafe18f8c8bd2bb":[1,0,0,8,16], -"qvector_8c.html#a304a57b5c428b4c517f1d0a22c84529b":[1,0,0,8,21], -"qvector_8c.html#a377aea2cd35c7d879a35fb3c9404e85c":[1,0,0,8,3], -"qvector_8c.html#a468e3d776f71310c1de156344ad67c12":[1,0,0,8,9], -"qvector_8c.html#a516f324b062fb12c15cc444b8f72798f":[1,0,0,8,4], -"qvector_8c.html#a69479bf55198bdd1c29e906d1b069480":[1,0,0,8,19], -"qvector_8c.html#a6aa6d22249f4cbeff4cbcb31aea99063":[1,0,0,8,18], -"qvector_8c.html#a6ed9e631160fd8f497a384dcb82d5794":[1,0,0,8,0], -"qvector_8c.html#a73dbecdb97c5825dedfd7fdd605cc0ad":[1,0,0,8,17], -"qvector_8c.html#a7968834024b83c5f7d4e76935d5d9a0c":[1,0,0,8,22], -"qvector_8c.html#a843cf9ea1d613a5eb28e7c7ac957b1cf":[1,0,0,8,7], -"qvector_8c.html#a888474c5ca93c96452782cddb03ada82":[1,0,0,8,24], -"qvector_8c.html#a9971c9a1bf24e25373b93fe2b8529d35":[1,0,0,8,6], -"qvector_8c.html#aa2643b2f497925b48e746ef58fc50729":[1,0,0,8,13], -"qvector_8c.html#adc720c91738ed960fac9c0675d5aafbe":[1,0,0,8,14], -"qvector_8c.html#add233a6a618bcb97d7216f706432445a":[1,0,0,8,11], -"qvector_8c.html#ae3290364eec4333b91c2b7314e6fee5a":[1,0,0,8,8], -"qvector_8c.html#aea2dd45d8692621f3aba1822b8f9e80e":[1,0,0,8,15], -"qvector_8c.html#af61312ebeacce01f8d2b1777802d3f98":[1,0,0,8,2], +"qvector_8c.html#a0bf76991577f397aafecb52599709103":[1,0,0,8,3], +"qvector_8c.html#a0da63e7c76f7cf42db2e5e027bd51a02":[1,0,0,8,14], +"qvector_8c.html#a11d7f8b4c7ac35bbfb60b168ae2a5046":[1,0,0,8,25], +"qvector_8c.html#a217d5fe3cb59560703d84e47097dc989":[1,0,0,8,22], +"qvector_8c.html#a251c32fb5bafe5e1beafe18f8c8bd2bb":[1,0,0,8,18], +"qvector_8c.html#a304a57b5c428b4c517f1d0a22c84529b":[1,0,0,8,23], +"qvector_8c.html#a4cb6c698932511561dba1574d0a18460":[1,0,0,8,1], +"qvector_8c.html#a51748a7e6721639ff37999b531c6e425":[1,0,0,8,0], +"qvector_8c.html#a5f7fac8917d39c653aaf57b0b5ef913b":[1,0,0,8,11], +"qvector_8c.html#a69479bf55198bdd1c29e906d1b069480":[1,0,0,8,21], +"qvector_8c.html#a6aa6d22249f4cbeff4cbcb31aea99063":[1,0,0,8,20], +"qvector_8c.html#a73dbecdb97c5825dedfd7fdd605cc0ad":[1,0,0,8,19], +"qvector_8c.html#a73f4f6085f1fa68e03451790e292436f":[1,0,0,8,7], +"qvector_8c.html#a7c361ad0a5caf3d3a4732cf087b178ab":[1,0,0,8,13], +"qvector_8c.html#a843cf9ea1d613a5eb28e7c7ac957b1cf":[1,0,0,8,9], +"qvector_8c.html#a888474c5ca93c96452782cddb03ada82":[1,0,0,8,26], +"qvector_8c.html#a96c7398c8e03e820b7f5d016639bc6f3":[1,0,0,8,5], +"qvector_8c.html#a9971c9a1bf24e25373b93fe2b8529d35":[1,0,0,8,8], +"qvector_8c.html#aa2643b2f497925b48e746ef58fc50729":[1,0,0,8,15], +"qvector_8c.html#ac8a262e93b18c9dc800f002389a9d645":[1,0,0,8,2], +"qvector_8c.html#ad93462633f13508a3c1345525e7776e2":[1,0,0,8,6], +"qvector_8c.html#adc720c91738ed960fac9c0675d5aafbe":[1,0,0,8,16], +"qvector_8c.html#ae3290364eec4333b91c2b7314e6fee5a":[1,0,0,8,10], +"qvector_8c.html#ae367e5e5fc33e2eb5dc9eb8791efcb15":[1,0,0,8,24], +"qvector_8c.html#ae501069d92ff2c9d9c852eb702346e69":[1,0,0,8,12], +"qvector_8c.html#aea2dd45d8692621f3aba1822b8f9e80e":[1,0,0,8,17], +"qvector_8c.html#af61312ebeacce01f8d2b1777802d3f98":[1,0,0,8,4], "qvector_8c_source.html":[1,0,0,8] }; diff --git a/doc/html/qaconf_8c.html b/doc/html/qaconf_8c.html index 222ac0a8..781692ff 100644 --- a/doc/html/qaconf_8c.html +++ b/doc/html/qaconf_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qaconf.c File Reference @@ -20,8 +20,8 @@
      - - + @@ -30,16 +30,15 @@
      +
      qLibc
      - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qaconf.c File Reference
    +
    +
    qaconf.c File Reference
    @@ -70,31 +70,31 @@

    Go to the source code of this file.

    - - - - - - + + + + + - - + + - - + + - - + + - - - - - + + + + + - - + +

    +

    Functions

    qaconf_t * qaconf (void)
     Create a new configuration object.
     
    static int addoptions (qaconf_t *qaconf, const qaconf_option_t *options)
     qaconf_t->addoptions(): Register option directives.
    qaconf_t * qaconf (void)
     Create a new configuration object. More...
     
    static int addoptions (qaconf_t *qaconf, const qaconf_option_t *options)
     qaconf_t->addoptions(): Register option directives. More...
     
    static void setdefhandler (qaconf_t *qaconf, qaconf_cb_t *callback)
     Set default callback function.
    static void setdefhandler (qaconf_t *qaconf, qaconf_cb_t *callback)
     Set default callback function. More...
     
    static void setuserdata (qaconf_t *qaconf, const void *userdata)
     qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    static void setuserdata (qaconf_t *qaconf, const void *userdata)
     qaconf_t->setuserdata(): Set userdata which will be provided on callback. More...
     
    static int parse (qaconf_t *qaconf, const char *filepath, uint8_t flags)
     qaconf_t->parse(): Run parser.
    static int parse (qaconf_t *qaconf, const char *filepath, uint8_t flags)
     qaconf_t->parse(): Run parser. More...
     
    static const char * errmsg (qaconf_t *qaconf)
     qaconf_t->errmsg(): Get last error message.
     
    static void reseterror (qaconf_t *qaconf)
     qaconf_t->reseterror(): Clear error message.
    static const char * errmsg (qaconf_t *qaconf)
     qaconf_t->errmsg(): Get last error message. More...
     
    static void reseterror (qaconf_t *qaconf)
     qaconf_t->reseterror(): Clear error message. More...
     
    static void free_ (qaconf_t *qaconf)
     qaconf_t->free(): Release resources.
    static void free_ (qaconf_t *qaconf)
     qaconf_t->free(): Release resources. More...
     

    Detailed Description

    @@ -164,7 +164,7 @@
    struct MyConf myconf;
    // Initialize and create a qaconf object.
    -
    qaconf_t *conf = qaconf();
    +
    qaconf_t *conf = qaconf();
    if (conf == NULL) {
    printf("Failed to open '" CONF_PATH "'.\n");
    return -1;
    @@ -225,7 +225,7 @@
    // Return OK
    return NULL;
    }
    -
    qaconf_t * qaconf(void)
    Create a new configuration object.
    Definition qaconf.c:245
    +
    qaconf_t * qaconf(void)
    Create a new configuration object.
    Definition: qaconf.c:245
    [Output]
    Listen [1:53]
    Protocols [1:UDP] [2:TCP]
    @@ -252,14 +252,14 @@

    Definition in file qaconf.c.

    Function Documentation

    - -

    ◆ qaconf()

    + +

    ◆ qaconf()

    - + @@ -270,7 +270,7 @@

    Returns
    a pointer of new qaconf_t object.
    -
    +
    qaconf_t *conf = qaconf();
    if (conf == NULL) {
    // Insufficient memory.
    }
    @@ -279,8 +279,8 @@

    -

    ◆ addoptions()

    + +

    ◆ addoptions()

    @@ -337,21 +337,21 @@

    };

    // Register options.
    -
    +
    qaconf_t *conf = qaconf();
    conf->addoptions(conf, options);
    (...codes goes...)

    It takes an array of options as provided in the sample codes. Each option consists of 5 parameters as below

    1st) Option Name : A option directive name.
    -
    2nd) Arguments : A number of arguments this option takes and their types.
    -
    3rd) Callback : A function pointer for callback.
    -
    4th) Section ID : Section ID if this option is a section like <Option>.
    -
    Otherwise 0 for regular option.
    -
    5th) Sections : ORed section IDs where this option can be specified.
    +
    2nd) Arguments : A number of arguments this option takes and their types.
    +
    3rd) Callback : A function pointer for callback.
    +
    4th) Section ID : Section ID if this option is a section like <Option>.
    +
    Otherwise 0 for regular option.
    +
    5th) Sections : ORed section IDs where this option can be specified.

    Example:

    {"TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST}
    1st) Option name is "TTL"
    2nd) It takes 1 argument and its type must be integer.
    -
    3rd) Callback function, confcb_debug, will be called.
    +
    3rd) Callback function, confcb_debug, will be called.
    4th) This is a regular option and does not have section id.
    5th) This option can be specified in OPT_SECTION_DOMAIN and OPT_SECTION_HOST.

    OPTION NAME field:

    @@ -362,7 +362,7 @@

    STR type : any type
    INT type : integer type. ex) 23, -12, 0
    FLOAT type : integer + floating point type. ex) 1.32, -32.5, 23, -12, 0
    -
    BOOL type : bool type ex) 1/0, true/false, on/off, yes/no
    +
    BOOL type : bool type ex) 1/0, true/false, on/off, yes/no

    When a BOOL type is specified, the argument passed to callback will be replaced to "1" or "0" for convenience use. For example, if "On" is specified as a argument and if BOOL type checking is specified, then actual argument which will be passed to callback will have "1". So we can simply determine it like "bool enabled = atoi(data->argv[1])".

    If original input argument needs to be passed to callback, specify STR type.

    Here is some examples of how to specify "Arguments" field.

    @@ -426,8 +426,8 @@

    -

    ◆ setdefhandler()

    + +

    ◆ setdefhandler()

    @@ -474,8 +474,8 @@

    -

    ◆ setuserdata()

    + +

    ◆ setuserdata()

    @@ -527,7 +527,7 @@

    (...codes...)

    // Set callback userdata.
    -
    conf->setuserdata(conf, &myconf);
    +
    conf->setuserdata(conf, &myconf);
    (...codes...)
    }
    @@ -539,14 +539,14 @@

    (...codes...)

    return NULL;
    }
    -
    static void setuserdata(qaconf_t *qaconf, const void *userdata)
    qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    Definition qaconf.c:506
    +
    static void setuserdata(qaconf_t *qaconf, const void *userdata)
    qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    Definition: qaconf.c:506

    Definition at line 506 of file qaconf.c.

    - -

    ◆ parse()

    + +

    ◆ parse()

    @@ -607,8 +607,8 @@

    -

    ◆ errmsg()

    + +

    ◆ errmsg()

    @@ -617,7 +617,7 @@

    qaconf_t * qaconf qaconf_t* qaconf ( void  )
    - + @@ -631,7 +631,7 @@

    -

    qaconf_t->errmsg(): Get last error message.

    +

    qaconf_t->errmsg(): Get last error message.

    Parameters

    static const char * errmsg static const char* errmsg ( qaconf_t *  qaconf)
    @@ -649,8 +649,8 @@

    -

    ◆ reseterror()

    + +

    ◆ reseterror()

    @@ -690,8 +690,8 @@

    -

    ◆ free_()

    + +

    ◆ free_()

    @@ -733,7 +733,7 @@

    diff --git a/doc/html/qaconf_8c.js b/doc/html/qaconf_8c.js index 7f180134..c3ed604d 100644 --- a/doc/html/qaconf_8c.js +++ b/doc/html/qaconf_8c.js @@ -1,11 +1,11 @@ var qaconf_8c = [ - [ "qaconf", "qaconf_8c.html#a8723c8602a9c7a93f6bae2bce7a03cc4", null ], + [ "qaconf", "qaconf_8c.html#ac28c668ae735bb483d2c61c59f54cae0", null ], [ "addoptions", "qaconf_8c.html#a4b7d9693f31e326b134f03f8ee4bca09", null ], [ "setdefhandler", "qaconf_8c.html#a440db32385534bf03f08de253737623b", null ], [ "setuserdata", "qaconf_8c.html#ab68c8ab80781fbca3aeaf7e3d16a9cc2", null ], [ "parse", "qaconf_8c.html#a1ee9ebbb7719b530a788d2d01256b2cc", null ], - [ "errmsg", "qaconf_8c.html#adb0ea6a3d4fb4d4734e098b42dfd0671", null ], + [ "errmsg", "qaconf_8c.html#a5c09256d455a4cf7443aca42bccdf0e0", null ], [ "reseterror", "qaconf_8c.html#abad17eff2cc816719fdba22f271b0aab", null ], [ "free_", "qaconf_8c.html#abfcdc4a8d1067527f4c2d99b29d0db39", null ] ]; \ No newline at end of file diff --git a/doc/html/qaconf_8c_source.html b/doc/html/qaconf_8c_source.html index 89907889..289e6fbc 100644 --- a/doc/html/qaconf_8c_source.html +++ b/doc/html/qaconf_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qaconf.c Source File @@ -20,8 +20,8 @@

    qaconfqaconf_t object.
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1059 +52,1060 @@
    -
    qaconf.c
    +
    +
    qaconf.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qaconf.c Apache-style configuration file parser.
    -
    31 *
    -
    32 * Apache-style Configuration is a configuration file syntax and format
    -
    33 * originally introduced by Apache HTTPd project. This format is powerful,
    -
    34 * flexible and human friendly. Even though this code gets distributed
    -
    35 * as a part of qLibc project, the code is written not to have any external
    -
    36 * dependencies to make this single file stands alone for better portability.
    -
    37 * It is purely written from the ground up and dedicated to the public
    -
    38 * by Seungyoung Kim.
    -
    39 *
    -
    40 * Sample Apache-style Configuration Syntax:
    -
    41 * @code
    -
    42 * # Lines that begin with the hash character "#" are considered comments.
    -
    43 * Listen 53
    -
    44 * Protocols UDP TCP
    -
    45 * IPSEC On
    -
    46 *
    -
    47 * <Domain "qdecoder.org">
    -
    48 * TTL 86400
    -
    49 * MX 10 mail.qdecoder.org
    -
    50 *
    -
    51 * <Host mail>
    -
    52 * IPv4 192.168.10.1
    -
    53 * TXT "US Rack-13D-18 \"San Jose's\""
    -
    54 * </Host>
    -
    55 *
    -
    56 * <Host www>
    -
    57 * IPv4 192.168.10.2
    -
    58 * TXT 'KR Rack-48H-31 "Seoul\'s"'
    -
    59 * TTL 3600
    -
    60 * </Host>
    -
    61 * </Domain>
    -
    62 *
    -
    63 * <Domain "ringfs.org">
    -
    64 * <Host www>
    -
    65 * CNAME www.qdecoder.org
    -
    66 * </Host>
    -
    67 * </Domain>
    -
    68 * @endcode
    -
    69 *
    -
    70 * @code
    -
    71 * // THIS EXAMPLE CODE CAN BE FOUND IN EXAMPLES DIRECTORY.
    -
    72 *
    -
    73 * // Define scope.
    -
    74 * // QAC_SCOPE_ALL and QAC_SCOPE_ROOT are predefined.
    -
    75 * // Custum scope should be defined from 2(1 << 1).
    -
    76 * // Note) These values are ORed(bit operation), so the number should be
    -
    77 * // 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
    -
    78 * enum {
    -
    79 * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
    -
    80 * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
    -
    81 * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
    -
    82 * OPT_SECTION_HOST = (1 << 2), // user-defined section
    -
    83 * };
    -
    84 *
    -
    85 * // Define callback proto-types.
    -
    86 * static QAC_CB(confcb_debug);
    -
    87 *
    -
    88 * // Define options and callbacks.
    -
    89 * static qaconf_option_t options[] = {
    -
    90 * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
    -
    91 * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
    -
    92 * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
    -
    93 * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
    -
    94 * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
    -
    95 * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
    -
    96 * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
    -
    97 * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    98 * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    99 * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    100 * QAC_OPTION_END
    -
    101 * };
    -
    102 *
    -
    103 * int user_main(void)
    -
    104 * {
    -
    105 * // Create a userdata structure.
    -
    106 * struct MyConf myconf;
    -
    107 *
    -
    108 * // Initialize and create a qaconf object.
    -
    109 * qaconf_t *conf = qaconf();
    -
    110 * if (conf == NULL) {
    -
    111 * printf("Failed to open '" CONF_PATH "'.\n");
    -
    112 * return -1;
    -
    113 * }
    -
    114 *
    -
    115 * // Register options.
    -
    116 * conf->addoptions(conf, options);
    -
    117 *
    -
    118 * // Set callback userdata
    -
    119 * // This is a userdata which will be provided on callback
    -
    120 * conf->setuserdata(conf, &myconf);
    -
    121 *
    -
    122 * // Run parser.
    -
    123 * int count = conf->parse(conf, CONF_PATH, QAC_CASEINSENSITIVE);
    -
    124 * if (count < 0) {
    -
    125 * printf("Error: %s\n", conf->errmsg(conf));
    -
    126 * } else {
    -
    127 * printf("Successfully loaded.\n");
    -
    128 * }
    -
    129 *
    -
    130 * // Verify userdata structure.
    -
    131 * if (conf->errmsg(conf) == NULL) { // another way to check parsing error.
    -
    132 * // codes here
    -
    133 * }
    -
    134 *
    -
    135 * // Release resources.
    -
    136 * conf->free(conf);
    -
    137 * }
    -
    138 *
    -
    139 * static QAC_CB(confcb_debug)
    -
    140 * {
    -
    141 * int i;
    -
    142 * for (i = 0; i < data->level; i++) {
    -
    143 * printf (" ");
    -
    144 * }
    -
    145 *
    -
    146 * // Print option name
    -
    147 * if (data->otype == QAC_OTYPE_SECTIONOPEN) {
    -
    148 * printf("<%s>", data->argv[0]);
    -
    149 * } else if (data->otype == QAC_OTYPE_SECTIONCLOSE) {
    -
    150 * printf("</%s>", data->argv[0]);
    -
    151 * } else { // This is QAC_OTYPE_OPTION type.
    -
    152 * printf("%s", data->argv[0]);
    -
    153 * }
    -
    154 *
    -
    155 * // Print parent names
    -
    156 * qaconf_cbdata_t *parent;
    -
    157 * for (parent = data->parent; parent != NULL; parent = parent->parent) {
    -
    158 * printf(" ::%s(%s)", parent->argv[0], parent->argv[1]);
    -
    159 * }
    -
    160 *
    -
    161 * // Print option arguments
    -
    162 * for (i = 1; i < data->argc; i++) {
    -
    163 * printf(" [%d:%s]", i, data->argv[i]);
    -
    164 * }
    -
    165 * printf("\n");
    -
    166 *
    -
    167 * // Return OK
    -
    168 * return NULL;
    -
    169 * }
    -
    170 * @endcode
    -
    171 *
    -
    172 * @code
    -
    173 * [Output]
    -
    174 * Listen [1:53]
    -
    175 * Protocols [1:UDP] [2:TCP]
    -
    176 * IPSEC [1:1]
    -
    177 * <Domain> [1:qdecoder.org]
    -
    178 * TTL ::Domain(qdecoder.org) [1:86400]
    -
    179 * MX ::Domain(qdecoder.org) [1:10] [2:mail.qdecoder.org]
    -
    180 * <Host> ::Domain(qdecoder.org) [1:mail]
    -
    181 * IPv4 ::Host(mail) ::Domain(qdecoder.org) [1:192.168.10.1]
    -
    182 * TXT ::Host(mail) ::Domain(qdecoder.org) [1:US Rack-13D-18 "San Jose's"]
    -
    183 * </Host> ::Domain(qdecoder.org) [1:mail]
    -
    184 * <Host> ::Domain(qdecoder.org) [1:www]
    -
    185 * IPv4 ::Host(www) ::Domain(qdecoder.org) [1:192.168.10.2]
    -
    186 * TXT ::Host(www) ::Domain(qdecoder.org) [1:KR Rack-48H-31 "Seoul's"]
    -
    187 * TTL ::Host(www) ::Domain(qdecoder.org) [1:3600]
    -
    188 * </Host> ::Domain(qdecoder.org) [1:www]
    -
    189 * </Domain> [1:qdecoder.org]
    -
    190 * <Domain> [1:ringfs.org]
    -
    191 * <Host> ::Domain(ringfs.org) [1:www]
    -
    192 * CNAME ::Host(www) ::Domain(ringfs.org) [1:www.qdecoder.org]
    -
    193 * </Host> ::Domain(ringfs.org) [1:www]
    -
    194 * </Domain> [1:ringfs.org]
    -
    195 * Successfully loaded.
    -
    196 * @endcode
    -
    197 */
    -
    198
    -
    199#ifndef DISABLE_QACONF
    -
    200
    -
    201#include <stdio.h>
    -
    202#include <stdlib.h>
    -
    203#include <stdbool.h>
    -
    204#include <stdarg.h>
    -
    205#include <string.h>
    -
    206#include <assert.h>
    -
    207#include <errno.h>
    -
    208#include "qinternal.h"
    -
    209#include "utilities/qstring.h"
    -
    210#include "extensions/qaconf.h"
    -
    211
    -
    212#ifndef _DOXYGEN_SKIP
    -
    213#define MAX_LINESIZE (1024*4)
    -
    214
    -
    215/* internal functions */
    -
    216static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options);
    -
    217static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback);
    -
    218static void setuserdata(qaconf_t *qaconf, const void *userdata);
    -
    219static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags);
    -
    220static const char *errmsg(qaconf_t *qaconf);
    -
    221static void reseterror(qaconf_t *qaconf);
    -
    222static void free_(qaconf_t *qaconf);
    -
    223
    -
    224static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
    -
    225 enum qaconf_section sectionid,
    -
    226 qaconf_cbdata_t *cbdata_parent);
    -
    227static void _seterrmsg(qaconf_t *qaconf, const char *format, ...);
    -
    228static void _free_cbdata(qaconf_cbdata_t *cbdata);
    -
    229static int _is_str_number(const char *s);
    -
    230static int _is_str_bool(const char *s);
    -
    231#endif
    -
    232
    -
    233/**
    -
    234 * Create a new configuration object.
    -
    235 *
    -
    236 * @return a pointer of new qaconf_t object.
    -
    237 *
    -
    238 * @code
    -
    239 * qaconf_t *conf = qaconf();
    -
    240 * if (conf == NULL) {
    -
    241 * // Insufficient memory.
    -
    242 * }
    -
    243 * @endcode
    -
    244 */
    -
    245qaconf_t *qaconf(void) {
    -
    246 // Malloc qaconf_t structure
    -
    247 qaconf_t *qaconf = (qaconf_t *) malloc(sizeof(qaconf_t));
    -
    248 if (qaconf == NULL)
    -
    249 return NULL;
    -
    250
    -
    251 // Initialize the structure
    -
    252 memset((void *) (qaconf), '\0', sizeof(qaconf_t));
    -
    253
    -
    254 // member methods
    -
    255 qaconf->addoptions = addoptions;
    -
    256 qaconf->setdefhandler = setdefhandler;
    -
    257 qaconf->setuserdata = setuserdata;
    -
    258 qaconf->parse = parse;
    -
    259 qaconf->errmsg = errmsg;
    -
    260 qaconf->reseterror = reseterror;
    -
    261 qaconf->free = free_;
    -
    262
    -
    263 return qaconf;
    -
    264}
    -
    265
    -
    266/**
    -
    267 * qaconf_t->addoptions(): Register option directives.
    -
    268 *
    -
    269 * @param qaconf qaconf_t object.
    -
    270 * @param options array pointer of qaconf_option_t.
    -
    271 *
    -
    272 * @return a number of options registered(added).
    -
    273 *
    -
    274 * @code
    -
    275 * qaconf_option_t options[] = {
    -
    276 * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
    -
    277 * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
    -
    278 * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
    -
    279 * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
    -
    280 * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
    -
    281 * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
    -
    282 * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
    -
    283 * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    284 * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    285 * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    -
    286 * QAC_OPTION_END
    -
    287 * };
    -
    288 *
    -
    289 * // Register options.
    -
    290 * qaconf_t *conf = qaconf();
    -
    291 * conf->addoptions(conf, options);
    -
    292 * (...codes goes...)
    -
    293 * @endcode
    -
    294 *
    -
    295 * It takes an array of options as provided in the sample codes.
    -
    296 * Each option consists of 5 parameters as below
    -
    297 *
    -
    298 * @code
    -
    299 * 1st) Option Name : A option directive name.
    -
    300 * 2nd) Arguments : A number of arguments this option takes and their types.
    -
    301 * 3rd) Callback : A function pointer for callback.
    -
    302 * 4th) Section ID : Section ID if this option is a section like <Option>.
    -
    303 * Otherwise 0 for regular option.
    -
    304 * 5th) Sections : ORed section IDs where this option can be specified.
    -
    305 * @endcode
    -
    306 *
    -
    307 * Example:
    -
    308 *
    -
    309 * @code
    -
    310 * {"TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST}
    -
    311 * 1st) Option name is "TTL"
    -
    312 * 2nd) It takes 1 argument and its type must be integer.
    -
    313 * 3rd) Callback function, confcb_debug, will be called.
    -
    314 * 4th) This is a regular option and does not have section id.
    -
    315 * 5th) This option can be specified in OPT_SECTION_DOMAIN and OPT_SECTION_HOST.
    -
    316 * @endcode
    -
    317 *
    -
    318 * OPTION NAME field:
    -
    319 *
    -
    320 * Option name is a unique string. Even an option is section type like <option>
    -
    321 * only name part without bracket needs to be specifed.
    -
    322 *
    -
    323 * ARGUMENT field:
    -
    324 *
    -
    325 * This field is for providing argument checking in parser level. So in user's
    -
    326 * callback routine can go simple. This provides checking of number of arguments
    -
    327 * this option can take and those argument type.
    -
    328 *
    -
    329 * In terms of argument types. There are 4 argument types as below.
    -
    330 * And first 5 arguments can be checked individually with different types.
    -
    331 *
    -
    332 * @code
    -
    333 * STR type : any type
    -
    334 * INT type : integer type. ex) 23, -12, 0
    -
    335 * FLOAT type : integer + floating point type. ex) 1.32, -32.5, 23, -12, 0
    -
    336 * BOOL type : bool type ex) 1/0, true/false, on/off, yes/no
    -
    337 * @endcode
    -
    338 *
    -
    339 * When a BOOL type is specified, the argument passed to callback will be
    -
    340 * replaced to "1" or "0" for convenience use. For example, if "On" is specified
    -
    341 * as a argument and if BOOL type checking is specified, then actual argument
    -
    342 * which will be passed to callback will have "1". So we can simply determine it
    -
    343 * like "bool enabled = atoi(data->argv[1])".
    -
    344 *
    -
    345 * If original input argument needs to be passed to callback, specify STR type.
    -
    346 *
    -
    347 * Here is some examples of how to specify "Arguments" field.
    -
    348 *
    -
    349 * @code
    -
    350 * An option takes 1 argument.
    -
    351 * QAC_TAKE_STR <= String(any) type
    -
    352 * QAC_TAKE_INT <= Integer type
    -
    353 * QAC_TAKE_FLOAT <= Float type
    -
    354 * QAC_TAKE_BOOL <= Bool type
    -
    355 *
    -
    356 * QAC_TAKE1 <= Equavalent to QAC_TAKE_STR
    -
    357 * QAC_TAKE1 | QAC_A1_BOOL <= Equavalent to QAC_TAKE_BOOL
    -
    358 *
    -
    359 * An option takes 2 arguments, bool and float.
    -
    360 * QAC_TAKE2 | QAC_A1_BOOL | QAC_A2_FLOAT
    -
    361 *
    -
    362 * An option takes any number of arguments in any type.
    -
    363 * QAC_TAKEALL
    -
    364 *
    -
    365 * An option takes any number of arguments but 1st one must be bool and
    -
    366 * 2nd one must be integer and rest of them must be float.
    -
    367 * QAC_TAKEALL | QAC_A1_BOOL | QAC_A2_INT | QAC_AA_FLOAT
    -
    368 * @endcode
    -
    369 *
    -
    370 * CALLBACK field:
    -
    371 *
    -
    372 * User defined callback function. We provide a macro, QAC_CB, for function
    -
    373 * proto type. Always use QAC_CB macro.
    -
    374 *
    -
    375 * @code
    -
    376 * QAC_CB(sample_cb) {
    -
    377 * (...codes...)
    -
    378 * }
    -
    379 *
    -
    380 * is equavalent to
    -
    381 *
    -
    382 * char *sample_cb(qaconf_cbdata_t *data, void *userdata) {
    -
    383 * (...codes...)
    -
    384 * }
    -
    385 * @endcode
    -
    386 *
    -
    387 * Callback function will be called with 2 arguments. One is callback data and
    -
    388 * the other one is userdata. Userdata is the data pointer set by setuserdata().
    -
    389 *
    -
    390 * Here is data structure. Arguments belong to the option can be accessed via
    -
    391 * argv variables like data->argv[1]. argv[0] is for the option name.
    -
    392 *
    -
    393 * @code
    -
    394 * struct qaconf_cbdata_s {
    -
    395 * enum qaconf_otype otype; // option type
    -
    396 * uint64_t section; // current section where this option is located
    -
    397 * uint64_t sections; // ORed all parent's sectionid(s) including current sections
    -
    398 * uint8_t level; // number of parents(level), root level is 0
    -
    399 * qaconf_cbdata_t *parent; // upper parent link
    -
    400 *
    -
    401 * int argc; // number arguments. always equal or greater than 1.
    -
    402 * char **argv; // argument pointers. argv[0] is option name.
    -
    403 * }
    -
    404 * @endcode
    -
    405 *
    -
    406 * SECTION ID field:
    -
    407 *
    -
    408 * If an option is an section like <Option>, section id can be assigned.
    -
    409 * This section id can be used to limit some other option directives to be
    -
    410 * located only inside of that section. So this is your choice. If it doesn't
    -
    411 * require to check directory scope, we can just specify 0 here.
    -
    412 *
    -
    413 * There are 2 pre-defined section id, QAC_SECTION_ALL and QAC_SECTION_ROOT.
    -
    414 * When we define user section, it has to be defined from 2(1 << 1)as below.
    -
    415 *
    -
    416 * @code
    -
    417 * enum {
    -
    418 * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
    -
    419 * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
    -
    420 * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
    -
    421 * OPT_SECTION_HOST = (1 << 2), // user-defined section
    -
    422 * };
    -
    423 * @endcode
    -
    424 *
    -
    425 * Please note that this section IDs are ORed. So the section id should be
    -
    426 * assigned in bit operation manner as 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
    -
    427 *
    -
    428 * SECTION IDS field:
    -
    429 *
    -
    430 * This field is to limit the scope where an option is allowed to be specified.
    -
    431 * Multiple section IDs can be ORed.
    -
    432 *
    -
    433 * QAC_SECTION_ALL means an option can be appeared in anywhere.
    -
    434 *
    -
    435 * QAC_SECTION_ROOT means an option can be appeared only in top level and not
    -
    436 * inside of any sections.
    -
    437 */
    -
    438static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options) {
    -
    439 if (qaconf == NULL || options == NULL) {
    -
    440 _seterrmsg(qaconf, "Invalid parameters.");
    -
    441 return -1;
    -
    442 }
    -
    443
    -
    444 // Count a number of options
    -
    445 uint32_t numopts;
    -
    446 for (numopts = 0; options[numopts].name != NULL; numopts++)
    -
    447 ;
    -
    448 if (numopts == 0)
    -
    449 return 0;
    -
    450
    -
    451 // Realloc
    -
    452 size_t newsize = sizeof(qaconf_option_t) * (qaconf->numoptions + numopts);
    -
    453 qaconf->options = (qaconf_option_t *) realloc(qaconf->options, newsize);
    -
    454 memcpy(&qaconf->options[qaconf->numoptions], options,
    -
    455 sizeof(qaconf_option_t) * numopts);
    -
    456 qaconf->numoptions += numopts;
    -
    457
    -
    458 return numopts;
    -
    459}
    -
    460
    -
    461/**
    -
    462 * Set default callback function.
    -
    463 *
    -
    464 * Default callback function will be called for unregistered option directives.
    -
    465 * QAC_IGNOREUNKNOWN flag will be ignored when default callback has set.
    -
    466 *
    -
    467 * @param qaconf qaconf_t object.
    -
    468 * @param callback callback function pointer
    -
    469 */
    -
    470static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback) {
    -
    471 qaconf->defcb = callback;
    -
    472}
    -
    473
    -
    474/**
    -
    475 * qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    -
    476 *
    -
    477 * @param qaconf qaconf_t object.
    -
    478 * @param userdata a pointer of userdata.
    -
    479 *
    -
    480 * @code
    -
    481 * // Define an example userdata
    -
    482 * struct MyConf {
    -
    483 * int sample;
    -
    484 * };
    -
    485 *
    -
    486 * int user_main(void) {
    -
    487 * struct MyConf myconf;
    -
    488 *
    -
    489 * (...codes...)
    -
    490 *
    -
    491 * // Set callback userdata.
    -
    492 * conf->setuserdata(conf, &myconf);
    -
    493 * (...codes...)
    -
    494 * }
    -
    495 *
    -
    496 * QAC_CB(confcb_callback_func) {
    -
    497 * (...codes...)
    -
    498 * // Type casting userdata for convenient use.
    -
    499 * struct MyConf *myconf = (struct MyConf *)userdata;
    -
    500 * myconf->sample++;
    -
    501 * (...codes...)
    -
    502 * return NULL;
    -
    503 * }
    -
    504 * @endcode
    -
    505 */
    -
    506static void setuserdata(qaconf_t *qaconf, const void *userdata) {
    -
    507 qaconf->userdata = (void *) userdata;
    -
    508}
    -
    509
    -
    510/**
    -
    511 * qaconf_t->parse(): Run parser.
    -
    512 *
    -
    513 * @param qaconf qaconf_t object.
    -
    514 * @param filepath configuration file path.
    -
    515 * @param flags parser options. (0 for default)
    -
    516 *
    -
    517 * @return A number of option directives parsed. -1 will be returned in case of
    -
    518 * error.
    -
    519 *
    -
    520 * Here is a list of flags. Multiple flags can be ORed.
    -
    521 *
    -
    522 * QAC_CASEINSENSITIVE: Option name is case-insensitive.
    -
    523 *
    -
    524 * QAC_IGNOREUNKNOWN : Ignore unknown option directives.
    -
    525 * This flag will be ignored if setdefhandler() has set.
    -
    526 *
    -
    527 * @code
    -
    528 * int c;
    -
    529 * c = conf->parse(conf, "sm1.conf", 0);
    -
    530 * c = conf->parse(conf, "sm2.conf", QAC_CASEINSENSITIVE);
    -
    531 * c = conf->parse(conf, "sm3.conf", QAC_CASEINSENSITIVE | QAC_IGNOREUNKNOWN);
    -
    532 * @endcode
    -
    533 */
    -
    534static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags) {
    -
    535 // Open file
    -
    536 FILE *fp = fopen(filepath, "r");
    -
    537 if (fp == NULL) {
    -
    538 _seterrmsg(qaconf, "Failed to open file '%s'.", filepath);
    -
    539 return -1;
    -
    540 }
    -
    541
    -
    542 // Set info
    -
    543 if (qaconf->filepath != NULL)
    -
    544 free(qaconf->filepath);
    -
    545 qaconf->filepath = strdup(filepath);
    -
    546 qaconf->lineno = 0;
    -
    547
    -
    548 // Parse
    -
    549 int optcount = _parse_inline(qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
    -
    550
    -
    551 // Clean up
    -
    552 fclose(fp);
    -
    553
    -
    554 return optcount;
    -
    555}
    -
    556
    -
    557/**
    -
    558 * qaconf_t->errmsg(): Get last error message.
    -
    559 *
    -
    560 * @param qaconf qaconf_t object.
    -
    561 *
    -
    562 * @return A const pointer of error message string.
    -
    563 *
    -
    564 * @code
    -
    565 * int c = conf->parse(conf, "sample.conf", 0);
    -
    566 * if (c < 0) {
    -
    567 * // ERROR
    -
    568 * printf("%s\n", conf->errmsg(conf));
    -
    569 * }
    -
    570 * @endcode
    -
    571 */
    -
    572static const char *errmsg(qaconf_t *qaconf) {
    -
    573 return (const char*) qaconf->errstr;
    -
    574}
    -
    575
    -
    576/**
    -
    577 * qaconf_t->reseterror(): Clear error message.
    -
    578 *
    -
    579 * @param qaconf qaconf_t object.
    -
    580 *
    -
    581 * @code
    -
    582 * conf->reseterror(conf);
    -
    583 * conf->parse(conf, "sample.conf", 0);
    -
    584 * if (conf->errmsg(conf) != NULL) {
    -
    585 * // ERROR
    -
    586 * }
    -
    587 * @endcode
    -
    588 */
    -
    589static void reseterror(qaconf_t *qaconf) {
    -
    590 if (qaconf->errstr != NULL) {
    -
    591 free(qaconf->errstr);
    -
    592 qaconf->errstr = NULL;
    -
    593 }
    -
    594}
    -
    595
    -
    596/**
    -
    597 * qaconf_t->free(): Release resources.
    -
    598 *
    -
    599 * @param qaconf qaconf_t object.
    -
    600 *
    -
    601 * @code
    -
    602 * conf->free(conf);
    -
    603 * @endcode
    -
    604 */
    -
    605static void free_(qaconf_t *qaconf) {
    -
    606 if (qaconf->filepath != NULL)
    -
    607 free(qaconf->filepath);
    -
    608 if (qaconf->errstr != NULL)
    -
    609 free(qaconf->errstr);
    -
    610 if (qaconf->options != NULL)
    -
    611 free(qaconf->options);
    -
    612 free(qaconf);
    -
    613}
    -
    614
    -
    615#ifndef _DOXYGEN_SKIP
    -
    616
    -
    617#define ARGV_INIT_SIZE (4)
    -
    618#define ARGV_INCR_STEP (8)
    -
    619#define MAX_TYPECHECK (5)
    -
    620static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
    -
    621 enum qaconf_section sectionid,
    -
    622 qaconf_cbdata_t *cbdata_parent) {
    -
    623 // Assign compare function.
    -
    624 int (*cmpfunc)(const char *, const char *) = strcmp;
    -
    625 if (flags & QAC_CASEINSENSITIVE)
    -
    626 cmpfunc = strcasecmp;
    -
    627
    -
    628 char buf[MAX_LINESIZE];
    -
    629 bool doneloop = false;
    -
    630 bool exception = false;
    -
    631 int optcount = 0; // number of option entry processed.
    -
    632 int newsectionid = 0; // temporary store
    -
    633 void *freethis = NULL; // userdata to free
    -
    634 while (doneloop == false && exception == false) {
    -
    635
    -
    636#define EXITLOOP(fmt, args...) do { \
    -
    637 _seterrmsg(qaconf, "%s:%d " fmt, \
    -
    638 qaconf->filepath, qaconf->lineno, ##args); \
    -
    639 exception = true; \
    -
    640 goto exitloop; \
    -
    641} while (0);
    -
    642
    -
    643 if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
    -
    644 // Check if section was opened and never closed
    -
    645 if (cbdata_parent != NULL) {
    -
    646 EXITLOOP("<%s> section was not closed.", cbdata_parent->argv[0]);
    -
    647 }
    -
    648 break;
    -
    649 }
    -
    650
    -
    651 // Increase line number counter
    -
    652 qaconf->lineno++;
    -
    653
    -
    654 // Trim white spaces
    -
    655 qstrtrim(buf);
    -
    656
    -
    657 // Skip blank like and comments.
    -
    658 if (IS_EMPTY_STR(buf) || *buf == '#') {
    -
    659 continue;
    -
    660 }
    -
    661
    -
    662 DEBUG("%s (line=%d)", buf, qaconf->lineno);
    -
    663
    -
    664 // Create a callback data
    -
    665 qaconf_cbdata_t *cbdata = (qaconf_cbdata_t*) malloc(
    -
    666 sizeof(qaconf_cbdata_t));
    -
    667 ASSERT(cbdata != NULL);
    -
    668 memset(cbdata, '\0', sizeof(qaconf_cbdata_t));
    -
    669 if (cbdata_parent != NULL) {
    -
    670 cbdata->section = sectionid;
    -
    671 cbdata->sections = cbdata_parent->sections | sectionid;
    -
    672 cbdata->level = cbdata_parent->level + 1;
    -
    673 cbdata->parent = cbdata_parent;
    -
    674 } else {
    -
    675 cbdata->section = sectionid;
    -
    676 cbdata->sections = sectionid;
    -
    677 cbdata->level = 0;
    -
    678 cbdata->parent = NULL;
    -
    679 }
    -
    680
    -
    681 // Escape section option
    -
    682 char *sp = buf;
    -
    683 if (*sp == '<') {
    -
    684 if (ENDING_CHAR(sp) != '>') {
    -
    685 EXITLOOP("Missing closing bracket. - '%s'.", buf);
    -
    686 }
    -
    687
    -
    688 sp++;
    -
    689 if (*sp == '/') {
    -
    690 cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
    -
    691 sp++;
    -
    692 } else {
    -
    693 cbdata->otype = QAC_OTYPE_SECTIONOPEN;
    -
    694 }
    -
    695
    -
    696 // Remove tailing bracket
    -
    697 ENDING_CHAR(sp) = '\0';
    -
    698 } else {
    -
    699 cbdata->otype = QAC_OTYPE_OPTION;
    -
    700 }
    -
    701
    -
    702 // Brackets has removed at this point
    -
    703 // Copy data into cbdata buffer.
    -
    704 cbdata->data = strdup(sp);
    -
    705 ASSERT(cbdata->data != NULL);
    -
    706
    -
    707 // Parse and tokenize.
    -
    708 int argvsize = 0;
    -
    709 char *wp1, *wp2;
    -
    710 bool doneparsing = false;
    -
    711 for (wp1 = (char *) cbdata->data; doneparsing == false; wp1 = wp2) {
    -
    712 // Allocate/Realloc argv array
    -
    713 if (argvsize == cbdata->argc) {
    -
    714 argvsize += (argvsize == 0) ? ARGV_INIT_SIZE : ARGV_INCR_STEP;
    -
    715 cbdata->argv = (char**) realloc((void *) cbdata->argv,
    -
    716 sizeof(char*) * argvsize);
    -
    717 ASSERT(cbdata->argv != NULL);
    -
    718 }
    -
    719
    -
    720 // Skip whitespaces
    -
    721 for (; (*wp1 == ' ' || *wp1 == '\t'); wp1++)
    -
    722 ;
    -
    723
    -
    724 // Quote handling
    -
    725 int qtmark = 0; // 1 for singlequotation, 2 for doublequotation
    -
    726 if (*wp1 == '\'') {
    -
    727 qtmark = 1;
    -
    728 wp1++;
    -
    729 } else if (*wp1 == '"') {
    -
    730 qtmark = 2;
    -
    731 wp1++;
    -
    732 }
    -
    733
    -
    734 // Parse a word
    -
    735 for (wp2 = wp1;; wp2++) {
    -
    736 if (*wp2 == '\0') {
    -
    737 doneparsing = true;
    -
    738 break;
    -
    739 } else if (*wp2 == '\'') {
    -
    740 if (qtmark == 1) {
    -
    741 qtmark = 0;
    -
    742 break;
    -
    743 }
    -
    744 } else if (*wp2 == '"') {
    -
    745 if (qtmark == 2) {
    -
    746 qtmark = 0;
    -
    747 break;
    -
    748 }
    -
    749 } else if (*wp2 == '\\') {
    -
    750 if (qtmark > 0) {
    -
    751 size_t wordlen = wp2 - wp1;
    -
    752 if (wordlen > 0)
    -
    753 memmove(wp1 + 1, wp1, wordlen);
    -
    754 wp1++;
    -
    755 wp2++;
    -
    756 }
    -
    757 } else if (*wp2 == ' ' || *wp2 == '\t') {
    -
    758 if (qtmark == 0)
    -
    759 break;
    -
    760 }
    -
    761 }
    -
    762 *wp2 = '\0';
    -
    763 wp2++;
    -
    764
    -
    765 // Check quotations has paired.
    -
    766 if (qtmark > 0) {
    -
    767 EXITLOOP("Quotation hasn't properly closed.");
    -
    768 }
    -
    769
    -
    770 // Store a argument
    -
    771 cbdata->argv[cbdata->argc] = wp1;
    -
    772 cbdata->argc++;
    -
    773 DEBUG(" argv[%d]=%s", cbdata->argc - 1, wp1);
    -
    774
    -
    775 // For quoted string, this case can be happened.
    -
    776 if (*wp2 == '\0') {
    -
    777 doneparsing = true;
    -
    778 }
    -
    779 }
    -
    780
    -
    781 // Check mismatch sectionclose
    -
    782 if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
    -
    783 if (cbdata_parent == NULL
    -
    784 || cmpfunc(cbdata->argv[0], cbdata_parent->argv[0])) {
    -
    785 EXITLOOP("Trying to close <%s> section that wasn't opened.",
    -
    786 cbdata->argv[0]);
    -
    787 }
    -
    788 }
    -
    789
    -
    790 // Find matching option
    -
    791 bool optfound = false;
    -
    792 int i;
    -
    793 for (i = 0; optfound == false && i < qaconf->numoptions; i++) {
    -
    794 qaconf_option_t *option = &qaconf->options[i];
    -
    795
    -
    796 if (!cmpfunc(cbdata->argv[0], option->name)) {
    -
    797 // Check sections
    -
    798 if ((cbdata->otype != QAC_OTYPE_SECTIONCLOSE)
    -
    799 && (option->sections != QAC_SECTION_ALL)
    -
    800 && (option->sections & sectionid) == 0) {
    -
    801 EXITLOOP("Option '%s' is in wrong section.", option->name);
    -
    802 }
    -
    803
    -
    804 // Check argument types
    -
    805 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
    -
    806 // Check number of arguments
    -
    807 int numtake = option->take & QAC_TAKEALL;
    -
    808 if (numtake != QAC_TAKEALL
    -
    809 && numtake != (cbdata->argc - 1)) {
    -
    810 EXITLOOP("'%s' option takes %d arguments.",
    -
    811 option->name, numtake);
    -
    812 }
    -
    813
    -
    814 // Check argument types
    -
    815 int deftype; // 0:str, 1:int, 2:float, 3:bool
    -
    816 if (option->take & QAC_AA_INT)
    -
    817 deftype = 1;
    -
    818 else if (option->take & QAC_AA_FLOAT)
    -
    819 deftype = 2;
    -
    820 else if (option->take & QAC_AA_BOOL)
    -
    821 deftype = 3;
    -
    822 else
    -
    823 deftype = 0;
    -
    824
    -
    825 int j;
    -
    826 for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
    -
    827 int argtype;
    -
    828 if (option->take & (QAC_A1_INT << (j - 1)))
    -
    829 argtype = 1;
    -
    830 else if (option->take & (QAC_A1_FLOAT << (j - 1)))
    -
    831 argtype = 2;
    -
    832 else if (option->take & (QAC_A1_BOOL << (j - 1)))
    -
    833 argtype = 3;
    -
    834 else
    -
    835 argtype = deftype;
    -
    836
    -
    837 if (argtype == 1) {
    -
    838 // integer type
    -
    839 if (_is_str_number(cbdata->argv[j]) != 1) {
    -
    840 EXITLOOP(
    -
    841 "%dth argument of '%s' must be integer type.",
    -
    842 j, option->name);
    -
    843 }
    -
    844 } else if (argtype == 2) {
    -
    845 // floating point type
    -
    846 if (_is_str_number(cbdata->argv[j]) == 0) {
    -
    847 EXITLOOP(
    -
    848 "%dth argument of '%s' must be floating point. type",
    -
    849 j, option->name);
    -
    850 }
    -
    851 } else if (argtype == 3) {
    -
    852 // bool type
    -
    853 if (_is_str_bool(cbdata->argv[j]) != 0) {
    -
    854 // Change argument to "1".
    -
    855 strcpy(cbdata->argv[j], "1");
    -
    856 } else {
    -
    857 EXITLOOP(
    -
    858 "%dth argument of '%s' must be bool type.",
    -
    859 j, option->name);
    -
    860 }
    -
    861 }
    -
    862 }
    -
    863 }
    -
    864
    -
    865 // Callback
    -
    866 //DEBUG("Callback %s", option->name);
    -
    867 qaconf_cb_t *usercb = option->cb;
    -
    868 if (usercb == NULL)
    -
    869 usercb = qaconf->defcb;
    -
    870 if (usercb != NULL) {
    -
    871 char *cberrmsg = NULL;
    -
    872
    -
    873 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
    -
    874 // Normal option and sectionopen
    -
    875 cberrmsg = usercb(cbdata, qaconf->userdata);
    -
    876 } else {
    -
    877 // QAC_OTYPE_SECTIONCLOSE
    -
    878
    -
    879 // Change otype
    -
    880 ASSERT(cbdata_parent != NULL);
    -
    881 enum qaconf_otype orig_otype = cbdata_parent->otype;
    -
    882 cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
    -
    883
    -
    884 // Callback
    -
    885 cberrmsg = usercb(cbdata_parent, qaconf->userdata);
    -
    886
    -
    887 // Restore type
    -
    888 cbdata_parent->otype = orig_otype;
    -
    889 }
    -
    890
    -
    891 // Error handling
    -
    892 if (cberrmsg != NULL) {
    -
    893 freethis = cberrmsg;
    -
    894 EXITLOOP("%s", cberrmsg);
    -
    895 }
    -
    896 }
    -
    897
    -
    898 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
    -
    899 // Store it for later
    -
    900 newsectionid = option->sectionid;
    -
    901 }
    -
    902
    -
    903 // Set found flag
    -
    904 optfound = true;
    -
    905 }
    -
    906 }
    -
    907
    -
    908 // If not found.
    -
    909 if (optfound == false) {
    -
    910 if (qaconf->defcb != NULL) {
    -
    911 qaconf->defcb(cbdata, qaconf->userdata);
    -
    912 } else if ((flags & QAC_IGNOREUNKNOWN) == 0) {
    -
    913 EXITLOOP("Unregistered option '%s'.", cbdata->argv[0]);
    -
    914 }
    -
    915 }
    -
    916
    -
    917 // Section handling
    -
    918 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
    -
    919 // Enter recursive call
    -
    920 DEBUG("Entering next level %d.", cbdata->level+1);
    -
    921 int optcount2 = _parse_inline(qaconf, fp, flags, newsectionid,
    -
    922 cbdata);
    -
    923 if (optcount2 >= 0) {
    -
    924 optcount += optcount2;
    -
    925 } else {
    -
    926 exception = true;
    -
    927 }DEBUG("Returned to previous level %d.", cbdata->level);
    -
    928 } else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
    -
    929 // Leave recursive call
    -
    930 doneloop = true;
    -
    931 }
    -
    932
    -
    933 exitloop:
    -
    934 // Release resources
    -
    935 if (freethis != NULL) {
    -
    936 free(freethis);
    -
    937 }
    -
    938
    -
    939 if (cbdata != NULL) {
    -
    940 _free_cbdata(cbdata);
    -
    941 cbdata = NULL;
    -
    942 }
    -
    943
    -
    944 if (exception == true) {
    -
    945 break;
    -
    946 }
    -
    947
    -
    948 // Go up and down
    -
    949 // if (otype
    -
    950
    -
    951 // Increase process counter
    -
    952 optcount++;
    -
    953 }
    -
    954
    -
    955 return (exception == false) ? optcount : -1;
    -
    956}
    -
    957
    -
    958static void _seterrmsg(qaconf_t *qaconf, const char *format, ...) {
    -
    959 if (qaconf->errstr != NULL)
    -
    960 free(qaconf->errstr);
    -
    961 DYNAMIC_VSPRINTF(qaconf->errstr, format);
    -
    962}
    -
    963
    -
    964static void _free_cbdata(qaconf_cbdata_t *cbdata) {
    -
    965 if (cbdata->argv != NULL)
    -
    966 free(cbdata->argv);
    -
    967 if (cbdata->data != NULL)
    -
    968 free(cbdata->data);
    -
    969 free(cbdata);
    -
    970}
    -
    971
    -
    972// return 2 for floating point .
    -
    973// return 1 for integer
    -
    974// return 0 for non number
    -
    975static int _is_str_number(const char *s) {
    -
    976 char *op = (char *) s;
    -
    977 if (*op == '-') {
    -
    978 op++;
    -
    979 }
    -
    980
    -
    981 char *cp, *dp;
    -
    982 for (cp = op, dp = NULL; *cp != '\0'; cp++) {
    -
    983 if ('0' <= *cp && *cp <= '9') {
    -
    984 continue;
    -
    985 }
    -
    986
    -
    987 if (*cp == '.') {
    -
    988 if (cp == op)
    -
    989 return 0; // dot can't be at the beginning.
    -
    990 if (dp != NULL)
    -
    991 return 0; // dot can't be appeared more than once.
    -
    992 dp = cp;
    -
    993 continue;
    -
    994 }
    -
    995
    -
    996 return 0;
    -
    997 }
    -
    998
    -
    999 if (cp == op) {
    -
    1000 return 0; // empty string
    -
    1001 }
    -
    1002
    -
    1003 if (dp != NULL) {
    -
    1004 if (dp + 1 == cp)
    -
    1005 return 0; // dot can't be at the end.
    -
    1006 return 2; // float point
    -
    1007 }
    -
    1008
    -
    1009 // integer
    -
    1010 return 1;
    -
    1011}
    -
    1012
    -
    1013static int _is_str_bool(const char *s) {
    -
    1014 if (!strcasecmp(s, "true"))
    -
    1015 return 1;
    -
    1016 else if (!strcasecmp(s, "on"))
    -
    1017 return 1;
    -
    1018 else if (!strcasecmp(s, "yes"))
    -
    1019 return 1;
    -
    1020 else if (!strcasecmp(s, "1"))
    -
    1021 return 1;
    -
    1022 return 0;
    -
    1023}
    -
    1024
    -
    1025#endif /* _DOXYGEN_SKIP */
    -
    1026
    -
    1027#endif /* DISABLE_QACONF */
    -
    1028
    -
    static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags)
    qaconf_t->parse(): Run parser.
    Definition qaconf.c:534
    -
    static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback)
    Set default callback function.
    Definition qaconf.c:470
    -
    static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options)
    qaconf_t->addoptions(): Register option directives.
    Definition qaconf.c:438
    -
    qaconf_t * qaconf(void)
    Create a new configuration object.
    Definition qaconf.c:245
    -
    static void setuserdata(qaconf_t *qaconf, const void *userdata)
    qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    Definition qaconf.c:506
    -
    static void reseterror(qaconf_t *qaconf)
    qaconf_t->reseterror(): Clear error message.
    Definition qaconf.c:589
    -
    static void free_(qaconf_t *qaconf)
    qaconf_t->free(): Release resources.
    Definition qaconf.c:605
    -
    static const char * errmsg(qaconf_t *qaconf)
    qaconf_t->errmsg(): Get last error message.
    Definition qaconf.c:572
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qaconf.c Apache-style configuration file parser.
    +
    31  *
    +
    32  * Apache-style Configuration is a configuration file syntax and format
    +
    33  * originally introduced by Apache HTTPd project. This format is powerful,
    +
    34  * flexible and human friendly. Even though this code gets distributed
    +
    35  * as a part of qLibc project, the code is written not to have any external
    +
    36  * dependencies to make this single file stands alone for better portability.
    +
    37  * It is purely written from the ground up and dedicated to the public
    +
    38  * by Seungyoung Kim.
    +
    39  *
    +
    40  * Sample Apache-style Configuration Syntax:
    +
    41  * @code
    +
    42  * # Lines that begin with the hash character "#" are considered comments.
    +
    43  * Listen 53
    +
    44  * Protocols UDP TCP
    +
    45  * IPSEC On
    +
    46  *
    +
    47  * <Domain "qdecoder.org">
    +
    48  * TTL 86400
    +
    49  * MX 10 mail.qdecoder.org
    +
    50  *
    +
    51  * <Host mail>
    +
    52  * IPv4 192.168.10.1
    +
    53  * TXT "US Rack-13D-18 \"San Jose's\""
    +
    54  * </Host>
    +
    55  *
    +
    56  * <Host www>
    +
    57  * IPv4 192.168.10.2
    +
    58  * TXT 'KR Rack-48H-31 "Seoul\'s"'
    +
    59  * TTL 3600
    +
    60  * </Host>
    +
    61  * </Domain>
    +
    62  *
    +
    63  * <Domain "ringfs.org">
    +
    64  * <Host www>
    +
    65  * CNAME www.qdecoder.org
    +
    66  * </Host>
    +
    67  * </Domain>
    +
    68  * @endcode
    +
    69  *
    +
    70  * @code
    +
    71  * // THIS EXAMPLE CODE CAN BE FOUND IN EXAMPLES DIRECTORY.
    +
    72  *
    +
    73  * // Define scope.
    +
    74  * // QAC_SCOPE_ALL and QAC_SCOPE_ROOT are predefined.
    +
    75  * // Custum scope should be defined from 2(1 << 1).
    +
    76  * // Note) These values are ORed(bit operation), so the number should be
    +
    77  * // 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
    +
    78  * enum {
    +
    79  * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
    +
    80  * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
    +
    81  * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
    +
    82  * OPT_SECTION_HOST = (1 << 2), // user-defined section
    +
    83  * };
    +
    84  *
    +
    85  * // Define callback proto-types.
    +
    86  * static QAC_CB(confcb_debug);
    +
    87  *
    +
    88  * // Define options and callbacks.
    +
    89  * static qaconf_option_t options[] = {
    +
    90  * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
    +
    91  * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
    +
    92  * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
    +
    93  * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
    +
    94  * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
    +
    95  * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
    +
    96  * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
    +
    97  * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    98  * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    99  * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    100  * QAC_OPTION_END
    +
    101  * };
    +
    102  *
    +
    103  * int user_main(void)
    +
    104  * {
    +
    105  * // Create a userdata structure.
    +
    106  * struct MyConf myconf;
    +
    107  *
    +
    108  * // Initialize and create a qaconf object.
    +
    109  * qaconf_t *conf = qaconf();
    +
    110  * if (conf == NULL) {
    +
    111  * printf("Failed to open '" CONF_PATH "'.\n");
    +
    112  * return -1;
    +
    113  * }
    +
    114  *
    +
    115  * // Register options.
    +
    116  * conf->addoptions(conf, options);
    +
    117  *
    +
    118  * // Set callback userdata
    +
    119  * // This is a userdata which will be provided on callback
    +
    120  * conf->setuserdata(conf, &myconf);
    +
    121  *
    +
    122  * // Run parser.
    +
    123  * int count = conf->parse(conf, CONF_PATH, QAC_CASEINSENSITIVE);
    +
    124  * if (count < 0) {
    +
    125  * printf("Error: %s\n", conf->errmsg(conf));
    +
    126  * } else {
    +
    127  * printf("Successfully loaded.\n");
    +
    128  * }
    +
    129  *
    +
    130  * // Verify userdata structure.
    +
    131  * if (conf->errmsg(conf) == NULL) { // another way to check parsing error.
    +
    132  * // codes here
    +
    133  * }
    +
    134  *
    +
    135  * // Release resources.
    +
    136  * conf->free(conf);
    +
    137  * }
    +
    138  *
    +
    139  * static QAC_CB(confcb_debug)
    +
    140  * {
    +
    141  * int i;
    +
    142  * for (i = 0; i < data->level; i++) {
    +
    143  * printf (" ");
    +
    144  * }
    +
    145  *
    +
    146  * // Print option name
    +
    147  * if (data->otype == QAC_OTYPE_SECTIONOPEN) {
    +
    148  * printf("<%s>", data->argv[0]);
    +
    149  * } else if (data->otype == QAC_OTYPE_SECTIONCLOSE) {
    +
    150  * printf("</%s>", data->argv[0]);
    +
    151  * } else { // This is QAC_OTYPE_OPTION type.
    +
    152  * printf("%s", data->argv[0]);
    +
    153  * }
    +
    154  *
    +
    155  * // Print parent names
    +
    156  * qaconf_cbdata_t *parent;
    +
    157  * for (parent = data->parent; parent != NULL; parent = parent->parent) {
    +
    158  * printf(" ::%s(%s)", parent->argv[0], parent->argv[1]);
    +
    159  * }
    +
    160  *
    +
    161  * // Print option arguments
    +
    162  * for (i = 1; i < data->argc; i++) {
    +
    163  * printf(" [%d:%s]", i, data->argv[i]);
    +
    164  * }
    +
    165  * printf("\n");
    +
    166  *
    +
    167  * // Return OK
    +
    168  * return NULL;
    +
    169  * }
    +
    170  * @endcode
    +
    171  *
    +
    172  * @code
    +
    173  * [Output]
    +
    174  * Listen [1:53]
    +
    175  * Protocols [1:UDP] [2:TCP]
    +
    176  * IPSEC [1:1]
    +
    177  * <Domain> [1:qdecoder.org]
    +
    178  * TTL ::Domain(qdecoder.org) [1:86400]
    +
    179  * MX ::Domain(qdecoder.org) [1:10] [2:mail.qdecoder.org]
    +
    180  * <Host> ::Domain(qdecoder.org) [1:mail]
    +
    181  * IPv4 ::Host(mail) ::Domain(qdecoder.org) [1:192.168.10.1]
    +
    182  * TXT ::Host(mail) ::Domain(qdecoder.org) [1:US Rack-13D-18 "San Jose's"]
    +
    183  * </Host> ::Domain(qdecoder.org) [1:mail]
    +
    184  * <Host> ::Domain(qdecoder.org) [1:www]
    +
    185  * IPv4 ::Host(www) ::Domain(qdecoder.org) [1:192.168.10.2]
    +
    186  * TXT ::Host(www) ::Domain(qdecoder.org) [1:KR Rack-48H-31 "Seoul's"]
    +
    187  * TTL ::Host(www) ::Domain(qdecoder.org) [1:3600]
    +
    188  * </Host> ::Domain(qdecoder.org) [1:www]
    +
    189  * </Domain> [1:qdecoder.org]
    +
    190  * <Domain> [1:ringfs.org]
    +
    191  * <Host> ::Domain(ringfs.org) [1:www]
    +
    192  * CNAME ::Host(www) ::Domain(ringfs.org) [1:www.qdecoder.org]
    +
    193  * </Host> ::Domain(ringfs.org) [1:www]
    +
    194  * </Domain> [1:ringfs.org]
    +
    195  * Successfully loaded.
    +
    196  * @endcode
    +
    197  */
    +
    198 
    +
    199 #ifndef DISABLE_QACONF
    +
    200 
    +
    201 #include <stdio.h>
    +
    202 #include <stdlib.h>
    +
    203 #include <stdbool.h>
    +
    204 #include <stdarg.h>
    +
    205 #include <string.h>
    +
    206 #include <assert.h>
    +
    207 #include <errno.h>
    +
    208 #include "qinternal.h"
    +
    209 #include "utilities/qstring.h"
    +
    210 #include "extensions/qaconf.h"
    +
    211 
    +
    212 #ifndef _DOXYGEN_SKIP
    +
    213 #define MAX_LINESIZE (1024*4)
    +
    214 
    +
    215 /* internal functions */
    +
    216 static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options);
    +
    217 static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback);
    +
    218 static void setuserdata(qaconf_t *qaconf, const void *userdata);
    +
    219 static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags);
    +
    220 static const char *errmsg(qaconf_t *qaconf);
    +
    221 static void reseterror(qaconf_t *qaconf);
    +
    222 static void free_(qaconf_t *qaconf);
    +
    223 
    +
    224 static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
    +
    225  enum qaconf_section sectionid,
    +
    226  qaconf_cbdata_t *cbdata_parent);
    +
    227 static void _seterrmsg(qaconf_t *qaconf, const char *format, ...);
    +
    228 static void _free_cbdata(qaconf_cbdata_t *cbdata);
    +
    229 static int _is_str_number(const char *s);
    +
    230 static int _is_str_bool(const char *s);
    +
    231 #endif
    +
    232 
    +
    233 /**
    +
    234  * Create a new configuration object.
    +
    235  *
    +
    236  * @return a pointer of new qaconf_t object.
    +
    237  *
    +
    238  * @code
    +
    239  * qaconf_t *conf = qaconf();
    +
    240  * if (conf == NULL) {
    +
    241  * // Insufficient memory.
    +
    242  * }
    +
    243  * @endcode
    +
    244  */
    +
    245 qaconf_t *qaconf(void) {
    +
    246  // Malloc qaconf_t structure
    +
    247  qaconf_t *qaconf = (qaconf_t *) malloc(sizeof(qaconf_t));
    +
    248  if (qaconf == NULL)
    +
    249  return NULL;
    +
    250 
    +
    251  // Initialize the structure
    +
    252  memset((void *) (qaconf), '\0', sizeof(qaconf_t));
    +
    253 
    +
    254  // member methods
    +
    255  qaconf->addoptions = addoptions;
    +
    256  qaconf->setdefhandler = setdefhandler;
    +
    257  qaconf->setuserdata = setuserdata;
    +
    258  qaconf->parse = parse;
    +
    259  qaconf->errmsg = errmsg;
    +
    260  qaconf->reseterror = reseterror;
    +
    261  qaconf->free = free_;
    +
    262 
    +
    263  return qaconf;
    +
    264 }
    +
    265 
    +
    266 /**
    +
    267  * qaconf_t->addoptions(): Register option directives.
    +
    268  *
    +
    269  * @param qaconf qaconf_t object.
    +
    270  * @param options array pointer of qaconf_option_t.
    +
    271  *
    +
    272  * @return a number of options registered(added).
    +
    273  *
    +
    274  * @code
    +
    275  * qaconf_option_t options[] = {
    +
    276  * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
    +
    277  * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
    +
    278  * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
    +
    279  * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
    +
    280  * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
    +
    281  * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
    +
    282  * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
    +
    283  * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    284  * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    285  * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
    +
    286  * QAC_OPTION_END
    +
    287  * };
    +
    288  *
    +
    289  * // Register options.
    +
    290  * qaconf_t *conf = qaconf();
    +
    291  * conf->addoptions(conf, options);
    +
    292  * (...codes goes...)
    +
    293  * @endcode
    +
    294  *
    +
    295  * It takes an array of options as provided in the sample codes.
    +
    296  * Each option consists of 5 parameters as below
    +
    297  *
    +
    298  * @code
    +
    299  * 1st) Option Name : A option directive name.
    +
    300  * 2nd) Arguments : A number of arguments this option takes and their types.
    +
    301  * 3rd) Callback : A function pointer for callback.
    +
    302  * 4th) Section ID : Section ID if this option is a section like <Option>.
    +
    303  * Otherwise 0 for regular option.
    +
    304  * 5th) Sections : ORed section IDs where this option can be specified.
    +
    305  * @endcode
    +
    306  *
    +
    307  * Example:
    +
    308  *
    +
    309  * @code
    +
    310  * {"TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST}
    +
    311  * 1st) Option name is "TTL"
    +
    312  * 2nd) It takes 1 argument and its type must be integer.
    +
    313  * 3rd) Callback function, confcb_debug, will be called.
    +
    314  * 4th) This is a regular option and does not have section id.
    +
    315  * 5th) This option can be specified in OPT_SECTION_DOMAIN and OPT_SECTION_HOST.
    +
    316  * @endcode
    +
    317  *
    +
    318  * OPTION NAME field:
    +
    319  *
    +
    320  * Option name is a unique string. Even an option is section type like <option>
    +
    321  * only name part without bracket needs to be specifed.
    +
    322  *
    +
    323  * ARGUMENT field:
    +
    324  *
    +
    325  * This field is for providing argument checking in parser level. So in user's
    +
    326  * callback routine can go simple. This provides checking of number of arguments
    +
    327  * this option can take and those argument type.
    +
    328  *
    +
    329  * In terms of argument types. There are 4 argument types as below.
    +
    330  * And first 5 arguments can be checked individually with different types.
    +
    331  *
    +
    332  * @code
    +
    333  * STR type : any type
    +
    334  * INT type : integer type. ex) 23, -12, 0
    +
    335  * FLOAT type : integer + floating point type. ex) 1.32, -32.5, 23, -12, 0
    +
    336  * BOOL type : bool type ex) 1/0, true/false, on/off, yes/no
    +
    337  * @endcode
    +
    338  *
    +
    339  * When a BOOL type is specified, the argument passed to callback will be
    +
    340  * replaced to "1" or "0" for convenience use. For example, if "On" is specified
    +
    341  * as a argument and if BOOL type checking is specified, then actual argument
    +
    342  * which will be passed to callback will have "1". So we can simply determine it
    +
    343  * like "bool enabled = atoi(data->argv[1])".
    +
    344  *
    +
    345  * If original input argument needs to be passed to callback, specify STR type.
    +
    346  *
    +
    347  * Here is some examples of how to specify "Arguments" field.
    +
    348  *
    +
    349  * @code
    +
    350  * An option takes 1 argument.
    +
    351  * QAC_TAKE_STR <= String(any) type
    +
    352  * QAC_TAKE_INT <= Integer type
    +
    353  * QAC_TAKE_FLOAT <= Float type
    +
    354  * QAC_TAKE_BOOL <= Bool type
    +
    355  *
    +
    356  * QAC_TAKE1 <= Equavalent to QAC_TAKE_STR
    +
    357  * QAC_TAKE1 | QAC_A1_BOOL <= Equavalent to QAC_TAKE_BOOL
    +
    358  *
    +
    359  * An option takes 2 arguments, bool and float.
    +
    360  * QAC_TAKE2 | QAC_A1_BOOL | QAC_A2_FLOAT
    +
    361  *
    +
    362  * An option takes any number of arguments in any type.
    +
    363  * QAC_TAKEALL
    +
    364  *
    +
    365  * An option takes any number of arguments but 1st one must be bool and
    +
    366  * 2nd one must be integer and rest of them must be float.
    +
    367  * QAC_TAKEALL | QAC_A1_BOOL | QAC_A2_INT | QAC_AA_FLOAT
    +
    368  * @endcode
    +
    369  *
    +
    370  * CALLBACK field:
    +
    371  *
    +
    372  * User defined callback function. We provide a macro, QAC_CB, for function
    +
    373  * proto type. Always use QAC_CB macro.
    +
    374  *
    +
    375  * @code
    +
    376  * QAC_CB(sample_cb) {
    +
    377  * (...codes...)
    +
    378  * }
    +
    379  *
    +
    380  * is equavalent to
    +
    381  *
    +
    382  * char *sample_cb(qaconf_cbdata_t *data, void *userdata) {
    +
    383  * (...codes...)
    +
    384  * }
    +
    385  * @endcode
    +
    386  *
    +
    387  * Callback function will be called with 2 arguments. One is callback data and
    +
    388  * the other one is userdata. Userdata is the data pointer set by setuserdata().
    +
    389  *
    +
    390  * Here is data structure. Arguments belong to the option can be accessed via
    +
    391  * argv variables like data->argv[1]. argv[0] is for the option name.
    +
    392  *
    +
    393  * @code
    +
    394  * struct qaconf_cbdata_s {
    +
    395  * enum qaconf_otype otype; // option type
    +
    396  * uint64_t section; // current section where this option is located
    +
    397  * uint64_t sections; // ORed all parent's sectionid(s) including current sections
    +
    398  * uint8_t level; // number of parents(level), root level is 0
    +
    399  * qaconf_cbdata_t *parent; // upper parent link
    +
    400  *
    +
    401  * int argc; // number arguments. always equal or greater than 1.
    +
    402  * char **argv; // argument pointers. argv[0] is option name.
    +
    403  * }
    +
    404  * @endcode
    +
    405  *
    +
    406  * SECTION ID field:
    +
    407  *
    +
    408  * If an option is an section like <Option>, section id can be assigned.
    +
    409  * This section id can be used to limit some other option directives to be
    +
    410  * located only inside of that section. So this is your choice. If it doesn't
    +
    411  * require to check directory scope, we can just specify 0 here.
    +
    412  *
    +
    413  * There are 2 pre-defined section id, QAC_SECTION_ALL and QAC_SECTION_ROOT.
    +
    414  * When we define user section, it has to be defined from 2(1 << 1)as below.
    +
    415  *
    +
    416  * @code
    +
    417  * enum {
    +
    418  * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
    +
    419  * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
    +
    420  * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
    +
    421  * OPT_SECTION_HOST = (1 << 2), // user-defined section
    +
    422  * };
    +
    423  * @endcode
    +
    424  *
    +
    425  * Please note that this section IDs are ORed. So the section id should be
    +
    426  * assigned in bit operation manner as 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
    +
    427  *
    +
    428  * SECTION IDS field:
    +
    429  *
    +
    430  * This field is to limit the scope where an option is allowed to be specified.
    +
    431  * Multiple section IDs can be ORed.
    +
    432  *
    +
    433  * QAC_SECTION_ALL means an option can be appeared in anywhere.
    +
    434  *
    +
    435  * QAC_SECTION_ROOT means an option can be appeared only in top level and not
    +
    436  * inside of any sections.
    +
    437  */
    +
    438 static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options) {
    +
    439  if (qaconf == NULL || options == NULL) {
    +
    440  _seterrmsg(qaconf, "Invalid parameters.");
    +
    441  return -1;
    +
    442  }
    +
    443 
    +
    444  // Count a number of options
    +
    445  uint32_t numopts;
    +
    446  for (numopts = 0; options[numopts].name != NULL; numopts++)
    +
    447  ;
    +
    448  if (numopts == 0)
    +
    449  return 0;
    +
    450 
    +
    451  // Realloc
    +
    452  size_t newsize = sizeof(qaconf_option_t) * (qaconf->numoptions + numopts);
    +
    453  qaconf->options = (qaconf_option_t *) realloc(qaconf->options, newsize);
    +
    454  memcpy(&qaconf->options[qaconf->numoptions], options,
    +
    455  sizeof(qaconf_option_t) * numopts);
    +
    456  qaconf->numoptions += numopts;
    +
    457 
    +
    458  return numopts;
    +
    459 }
    +
    460 
    +
    461 /**
    +
    462  * Set default callback function.
    +
    463  *
    +
    464  * Default callback function will be called for unregistered option directives.
    +
    465  * QAC_IGNOREUNKNOWN flag will be ignored when default callback has set.
    +
    466  *
    +
    467  * @param qaconf qaconf_t object.
    +
    468  * @param callback callback function pointer
    +
    469  */
    +
    470 static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback) {
    +
    471  qaconf->defcb = callback;
    +
    472 }
    +
    473 
    +
    474 /**
    +
    475  * qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    +
    476  *
    +
    477  * @param qaconf qaconf_t object.
    +
    478  * @param userdata a pointer of userdata.
    +
    479  *
    +
    480  * @code
    +
    481  * // Define an example userdata
    +
    482  * struct MyConf {
    +
    483  * int sample;
    +
    484  * };
    +
    485  *
    +
    486  * int user_main(void) {
    +
    487  * struct MyConf myconf;
    +
    488  *
    +
    489  * (...codes...)
    +
    490  *
    +
    491  * // Set callback userdata.
    +
    492  * conf->setuserdata(conf, &myconf);
    +
    493  * (...codes...)
    +
    494  * }
    +
    495  *
    +
    496  * QAC_CB(confcb_callback_func) {
    +
    497  * (...codes...)
    +
    498  * // Type casting userdata for convenient use.
    +
    499  * struct MyConf *myconf = (struct MyConf *)userdata;
    +
    500  * myconf->sample++;
    +
    501  * (...codes...)
    +
    502  * return NULL;
    +
    503  * }
    +
    504  * @endcode
    +
    505  */
    +
    506 static void setuserdata(qaconf_t *qaconf, const void *userdata) {
    +
    507  qaconf->userdata = (void *) userdata;
    +
    508 }
    +
    509 
    +
    510 /**
    +
    511  * qaconf_t->parse(): Run parser.
    +
    512  *
    +
    513  * @param qaconf qaconf_t object.
    +
    514  * @param filepath configuration file path.
    +
    515  * @param flags parser options. (0 for default)
    +
    516  *
    +
    517  * @return A number of option directives parsed. -1 will be returned in case of
    +
    518  * error.
    +
    519  *
    +
    520  * Here is a list of flags. Multiple flags can be ORed.
    +
    521  *
    +
    522  * QAC_CASEINSENSITIVE: Option name is case-insensitive.
    +
    523  *
    +
    524  * QAC_IGNOREUNKNOWN : Ignore unknown option directives.
    +
    525  * This flag will be ignored if setdefhandler() has set.
    +
    526  *
    +
    527  * @code
    +
    528  * int c;
    +
    529  * c = conf->parse(conf, "sm1.conf", 0);
    +
    530  * c = conf->parse(conf, "sm2.conf", QAC_CASEINSENSITIVE);
    +
    531  * c = conf->parse(conf, "sm3.conf", QAC_CASEINSENSITIVE | QAC_IGNOREUNKNOWN);
    +
    532  * @endcode
    +
    533  */
    +
    534 static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags) {
    +
    535  // Open file
    +
    536  FILE *fp = fopen(filepath, "r");
    +
    537  if (fp == NULL) {
    +
    538  _seterrmsg(qaconf, "Failed to open file '%s'.", filepath);
    +
    539  return -1;
    +
    540  }
    +
    541 
    +
    542  // Set info
    +
    543  if (qaconf->filepath != NULL)
    +
    544  free(qaconf->filepath);
    +
    545  qaconf->filepath = strdup(filepath);
    +
    546  qaconf->lineno = 0;
    +
    547 
    +
    548  // Parse
    +
    549  int optcount = _parse_inline(qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
    +
    550 
    +
    551  // Clean up
    +
    552  fclose(fp);
    +
    553 
    +
    554  return optcount;
    +
    555 }
    +
    556 
    +
    557 /**
    +
    558  * qaconf_t->errmsg(): Get last error message.
    +
    559  *
    +
    560  * @param qaconf qaconf_t object.
    +
    561  *
    +
    562  * @return A const pointer of error message string.
    +
    563  *
    +
    564  * @code
    +
    565  * int c = conf->parse(conf, "sample.conf", 0);
    +
    566  * if (c < 0) {
    +
    567  * // ERROR
    +
    568  * printf("%s\n", conf->errmsg(conf));
    +
    569  * }
    +
    570  * @endcode
    +
    571  */
    +
    572 static const char *errmsg(qaconf_t *qaconf) {
    +
    573  return (const char*) qaconf->errstr;
    +
    574 }
    +
    575 
    +
    576 /**
    +
    577  * qaconf_t->reseterror(): Clear error message.
    +
    578  *
    +
    579  * @param qaconf qaconf_t object.
    +
    580  *
    +
    581  * @code
    +
    582  * conf->reseterror(conf);
    +
    583  * conf->parse(conf, "sample.conf", 0);
    +
    584  * if (conf->errmsg(conf) != NULL) {
    +
    585  * // ERROR
    +
    586  * }
    +
    587  * @endcode
    +
    588  */
    +
    589 static void reseterror(qaconf_t *qaconf) {
    +
    590  if (qaconf->errstr != NULL) {
    +
    591  free(qaconf->errstr);
    +
    592  qaconf->errstr = NULL;
    +
    593  }
    +
    594 }
    +
    595 
    +
    596 /**
    +
    597  * qaconf_t->free(): Release resources.
    +
    598  *
    +
    599  * @param qaconf qaconf_t object.
    +
    600  *
    +
    601  * @code
    +
    602  * conf->free(conf);
    +
    603  * @endcode
    +
    604  */
    +
    605 static void free_(qaconf_t *qaconf) {
    +
    606  if (qaconf->filepath != NULL)
    +
    607  free(qaconf->filepath);
    +
    608  if (qaconf->errstr != NULL)
    +
    609  free(qaconf->errstr);
    +
    610  if (qaconf->options != NULL)
    +
    611  free(qaconf->options);
    +
    612  free(qaconf);
    +
    613 }
    +
    614 
    +
    615 #ifndef _DOXYGEN_SKIP
    +
    616 
    +
    617 #define ARGV_INIT_SIZE (4)
    +
    618 #define ARGV_INCR_STEP (8)
    +
    619 #define MAX_TYPECHECK (5)
    +
    620 static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
    +
    621  enum qaconf_section sectionid,
    +
    622  qaconf_cbdata_t *cbdata_parent) {
    +
    623  // Assign compare function.
    +
    624  int (*cmpfunc)(const char *, const char *) = strcmp;
    +
    625  if (flags & QAC_CASEINSENSITIVE)
    +
    626  cmpfunc = strcasecmp;
    +
    627 
    +
    628  char buf[MAX_LINESIZE];
    +
    629  bool doneloop = false;
    +
    630  bool exception = false;
    +
    631  int optcount = 0; // number of option entry processed.
    +
    632  int newsectionid = 0; // temporary store
    +
    633  void *freethis = NULL; // userdata to free
    +
    634  while (doneloop == false && exception == false) {
    +
    635 
    +
    636 #define EXITLOOP(fmt, args...) do { \
    +
    637  _seterrmsg(qaconf, "%s:%d " fmt, \
    +
    638  qaconf->filepath, qaconf->lineno, ##args); \
    +
    639  exception = true; \
    +
    640  goto exitloop; \
    +
    641 } while (0);
    +
    642 
    +
    643  if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
    +
    644  // Check if section was opened and never closed
    +
    645  if (cbdata_parent != NULL) {
    +
    646  EXITLOOP("<%s> section was not closed.", cbdata_parent->argv[0]);
    +
    647  }
    +
    648  break;
    +
    649  }
    +
    650 
    +
    651  // Increase line number counter
    +
    652  qaconf->lineno++;
    +
    653 
    +
    654  // Trim white spaces
    +
    655  qstrtrim(buf);
    +
    656 
    +
    657  // Skip blank like and comments.
    +
    658  if (IS_EMPTY_STR(buf) || *buf == '#') {
    +
    659  continue;
    +
    660  }
    +
    661 
    +
    662  DEBUG("%s (line=%d)", buf, qaconf->lineno);
    +
    663 
    +
    664  // Create a callback data
    +
    665  qaconf_cbdata_t *cbdata = (qaconf_cbdata_t*) malloc(
    +
    666  sizeof(qaconf_cbdata_t));
    +
    667  ASSERT(cbdata != NULL);
    +
    668  memset(cbdata, '\0', sizeof(qaconf_cbdata_t));
    +
    669  if (cbdata_parent != NULL) {
    +
    670  cbdata->section = sectionid;
    +
    671  cbdata->sections = cbdata_parent->sections | sectionid;
    +
    672  cbdata->level = cbdata_parent->level + 1;
    +
    673  cbdata->parent = cbdata_parent;
    +
    674  } else {
    +
    675  cbdata->section = sectionid;
    +
    676  cbdata->sections = sectionid;
    +
    677  cbdata->level = 0;
    +
    678  cbdata->parent = NULL;
    +
    679  }
    +
    680 
    +
    681  // Escape section option
    +
    682  char *sp = buf;
    +
    683  if (*sp == '<') {
    +
    684  if (ENDING_CHAR(sp) != '>') {
    +
    685  EXITLOOP("Missing closing bracket. - '%s'.", buf);
    +
    686  }
    +
    687 
    +
    688  sp++;
    +
    689  if (*sp == '/') {
    +
    690  cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
    +
    691  sp++;
    +
    692  } else {
    +
    693  cbdata->otype = QAC_OTYPE_SECTIONOPEN;
    +
    694  }
    +
    695 
    +
    696  // Remove tailing bracket
    +
    697  ENDING_CHAR(sp) = '\0';
    +
    698  } else {
    +
    699  cbdata->otype = QAC_OTYPE_OPTION;
    +
    700  }
    +
    701 
    +
    702  // Brackets has removed at this point
    +
    703  // Copy data into cbdata buffer.
    +
    704  cbdata->data = strdup(sp);
    +
    705  ASSERT(cbdata->data != NULL);
    +
    706 
    +
    707  // Parse and tokenize.
    +
    708  int argvsize = 0;
    +
    709  char *wp1, *wp2;
    +
    710  bool doneparsing = false;
    +
    711  for (wp1 = (char *) cbdata->data; doneparsing == false; wp1 = wp2) {
    +
    712  // Allocate/Realloc argv array
    +
    713  if (argvsize == cbdata->argc) {
    +
    714  argvsize += (argvsize == 0) ? ARGV_INIT_SIZE : ARGV_INCR_STEP;
    +
    715  cbdata->argv = (char**) realloc((void *) cbdata->argv,
    +
    716  sizeof(char*) * argvsize);
    +
    717  ASSERT(cbdata->argv != NULL);
    +
    718  }
    +
    719 
    +
    720  // Skip whitespaces
    +
    721  for (; (*wp1 == ' ' || *wp1 == '\t'); wp1++)
    +
    722  ;
    +
    723 
    +
    724  // Quote handling
    +
    725  int qtmark = 0; // 1 for singlequotation, 2 for doublequotation
    +
    726  if (*wp1 == '\'') {
    +
    727  qtmark = 1;
    +
    728  wp1++;
    +
    729  } else if (*wp1 == '"') {
    +
    730  qtmark = 2;
    +
    731  wp1++;
    +
    732  }
    +
    733 
    +
    734  // Parse a word
    +
    735  for (wp2 = wp1;; wp2++) {
    +
    736  if (*wp2 == '\0') {
    +
    737  doneparsing = true;
    +
    738  break;
    +
    739  } else if (*wp2 == '\'') {
    +
    740  if (qtmark == 1) {
    +
    741  qtmark = 0;
    +
    742  break;
    +
    743  }
    +
    744  } else if (*wp2 == '"') {
    +
    745  if (qtmark == 2) {
    +
    746  qtmark = 0;
    +
    747  break;
    +
    748  }
    +
    749  } else if (*wp2 == '\\') {
    +
    750  if (qtmark > 0) {
    +
    751  size_t wordlen = wp2 - wp1;
    +
    752  if (wordlen > 0)
    +
    753  memmove(wp1 + 1, wp1, wordlen);
    +
    754  wp1++;
    +
    755  wp2++;
    +
    756  }
    +
    757  } else if (*wp2 == ' ' || *wp2 == '\t') {
    +
    758  if (qtmark == 0)
    +
    759  break;
    +
    760  }
    +
    761  }
    +
    762  *wp2 = '\0';
    +
    763  wp2++;
    +
    764 
    +
    765  // Check quotations has paired.
    +
    766  if (qtmark > 0) {
    +
    767  EXITLOOP("Quotation hasn't properly closed.");
    +
    768  }
    +
    769 
    +
    770  // Store a argument
    +
    771  cbdata->argv[cbdata->argc] = wp1;
    +
    772  cbdata->argc++;
    +
    773  DEBUG(" argv[%d]=%s", cbdata->argc - 1, wp1);
    +
    774 
    +
    775  // For quoted string, this case can be happened.
    +
    776  if (*wp2 == '\0') {
    +
    777  doneparsing = true;
    +
    778  }
    +
    779  }
    +
    780 
    +
    781  // Check mismatch sectionclose
    +
    782  if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
    +
    783  if (cbdata_parent == NULL
    +
    784  || cmpfunc(cbdata->argv[0], cbdata_parent->argv[0])) {
    +
    785  EXITLOOP("Trying to close <%s> section that wasn't opened.",
    +
    786  cbdata->argv[0]);
    +
    787  }
    +
    788  }
    +
    789 
    +
    790  // Find matching option
    +
    791  bool optfound = false;
    +
    792  int i;
    +
    793  for (i = 0; optfound == false && i < qaconf->numoptions; i++) {
    +
    794  qaconf_option_t *option = &qaconf->options[i];
    +
    795 
    +
    796  if (!cmpfunc(cbdata->argv[0], option->name)) {
    +
    797  // Check sections
    +
    798  if ((cbdata->otype != QAC_OTYPE_SECTIONCLOSE)
    +
    799  && (option->sections != QAC_SECTION_ALL)
    +
    800  && (option->sections & sectionid) == 0) {
    +
    801  EXITLOOP("Option '%s' is in wrong section.", option->name);
    +
    802  }
    +
    803 
    +
    804  // Check argument types
    +
    805  if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
    +
    806  // Check number of arguments
    +
    807  int numtake = option->take & QAC_TAKEALL;
    +
    808  if (numtake != QAC_TAKEALL
    +
    809  && numtake != (cbdata->argc - 1)) {
    +
    810  EXITLOOP("'%s' option takes %d arguments.",
    +
    811  option->name, numtake);
    +
    812  }
    +
    813 
    +
    814  // Check argument types
    +
    815  int deftype; // 0:str, 1:int, 2:float, 3:bool
    +
    816  if (option->take & QAC_AA_INT)
    +
    817  deftype = 1;
    +
    818  else if (option->take & QAC_AA_FLOAT)
    +
    819  deftype = 2;
    +
    820  else if (option->take & QAC_AA_BOOL)
    +
    821  deftype = 3;
    +
    822  else
    +
    823  deftype = 0;
    +
    824 
    +
    825  int j;
    +
    826  for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
    +
    827  int argtype;
    +
    828  if (option->take & (QAC_A1_INT << (j - 1)))
    +
    829  argtype = 1;
    +
    830  else if (option->take & (QAC_A1_FLOAT << (j - 1)))
    +
    831  argtype = 2;
    +
    832  else if (option->take & (QAC_A1_BOOL << (j - 1)))
    +
    833  argtype = 3;
    +
    834  else
    +
    835  argtype = deftype;
    +
    836 
    +
    837  if (argtype == 1) {
    +
    838  // integer type
    +
    839  if (_is_str_number(cbdata->argv[j]) != 1) {
    +
    840  EXITLOOP(
    +
    841  "%dth argument of '%s' must be integer type.",
    +
    842  j, option->name);
    +
    843  }
    +
    844  } else if (argtype == 2) {
    +
    845  // floating point type
    +
    846  if (_is_str_number(cbdata->argv[j]) == 0) {
    +
    847  EXITLOOP(
    +
    848  "%dth argument of '%s' must be floating point. type",
    +
    849  j, option->name);
    +
    850  }
    +
    851  } else if (argtype == 3) {
    +
    852  // bool type
    +
    853  if (_is_str_bool(cbdata->argv[j]) != 0) {
    +
    854  // Change argument to "1".
    +
    855  strcpy(cbdata->argv[j], "1");
    +
    856  } else {
    +
    857  EXITLOOP(
    +
    858  "%dth argument of '%s' must be bool type.",
    +
    859  j, option->name);
    +
    860  }
    +
    861  }
    +
    862  }
    +
    863  }
    +
    864 
    +
    865  // Callback
    +
    866  //DEBUG("Callback %s", option->name);
    +
    867  qaconf_cb_t *usercb = option->cb;
    +
    868  if (usercb == NULL)
    +
    869  usercb = qaconf->defcb;
    +
    870  if (usercb != NULL) {
    +
    871  char *cberrmsg = NULL;
    +
    872 
    +
    873  if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
    +
    874  // Normal option and sectionopen
    +
    875  cberrmsg = usercb(cbdata, qaconf->userdata);
    +
    876  } else {
    +
    877  // QAC_OTYPE_SECTIONCLOSE
    +
    878 
    +
    879  // Change otype
    +
    880  ASSERT(cbdata_parent != NULL);
    +
    881  enum qaconf_otype orig_otype = cbdata_parent->otype;
    +
    882  cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
    +
    883 
    +
    884  // Callback
    +
    885  cberrmsg = usercb(cbdata_parent, qaconf->userdata);
    +
    886 
    +
    887  // Restore type
    +
    888  cbdata_parent->otype = orig_otype;
    +
    889  }
    +
    890 
    +
    891  // Error handling
    +
    892  if (cberrmsg != NULL) {
    +
    893  freethis = cberrmsg;
    +
    894  EXITLOOP("%s", cberrmsg);
    +
    895  }
    +
    896  }
    +
    897 
    +
    898  if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
    +
    899  // Store it for later
    +
    900  newsectionid = option->sectionid;
    +
    901  }
    +
    902 
    +
    903  // Set found flag
    +
    904  optfound = true;
    +
    905  }
    +
    906  }
    +
    907 
    +
    908  // If not found.
    +
    909  if (optfound == false) {
    +
    910  if (qaconf->defcb != NULL) {
    +
    911  qaconf->defcb(cbdata, qaconf->userdata);
    +
    912  } else if ((flags & QAC_IGNOREUNKNOWN) == 0) {
    +
    913  EXITLOOP("Unregistered option '%s'.", cbdata->argv[0]);
    +
    914  }
    +
    915  }
    +
    916 
    +
    917  // Section handling
    +
    918  if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
    +
    919  // Enter recursive call
    +
    920  DEBUG("Entering next level %d.", cbdata->level+1);
    +
    921  int optcount2 = _parse_inline(qaconf, fp, flags, newsectionid,
    +
    922  cbdata);
    +
    923  if (optcount2 >= 0) {
    +
    924  optcount += optcount2;
    +
    925  } else {
    +
    926  exception = true;
    +
    927  }DEBUG("Returned to previous level %d.", cbdata->level);
    +
    928  } else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
    +
    929  // Leave recursive call
    +
    930  doneloop = true;
    +
    931  }
    +
    932 
    +
    933  exitloop:
    +
    934  // Release resources
    +
    935  if (freethis != NULL) {
    +
    936  free(freethis);
    +
    937  }
    +
    938 
    +
    939  if (cbdata != NULL) {
    +
    940  _free_cbdata(cbdata);
    +
    941  cbdata = NULL;
    +
    942  }
    +
    943 
    +
    944  if (exception == true) {
    +
    945  break;
    +
    946  }
    +
    947 
    +
    948  // Go up and down
    +
    949  // if (otype
    +
    950 
    +
    951  // Increase process counter
    +
    952  optcount++;
    +
    953  }
    +
    954 
    +
    955  return (exception == false) ? optcount : -1;
    +
    956 }
    +
    957 
    +
    958 static void _seterrmsg(qaconf_t *qaconf, const char *format, ...) {
    +
    959  if (qaconf->errstr != NULL)
    +
    960  free(qaconf->errstr);
    +
    961  DYNAMIC_VSPRINTF(qaconf->errstr, format);
    +
    962 }
    +
    963 
    +
    964 static void _free_cbdata(qaconf_cbdata_t *cbdata) {
    +
    965  if (cbdata->argv != NULL)
    +
    966  free(cbdata->argv);
    +
    967  if (cbdata->data != NULL)
    +
    968  free(cbdata->data);
    +
    969  free(cbdata);
    +
    970 }
    +
    971 
    +
    972 // return 2 for floating point .
    +
    973 // return 1 for integer
    +
    974 // return 0 for non number
    +
    975 static int _is_str_number(const char *s) {
    +
    976  char *op = (char *) s;
    +
    977  if (*op == '-') {
    +
    978  op++;
    +
    979  }
    +
    980 
    +
    981  char *cp, *dp;
    +
    982  for (cp = op, dp = NULL; *cp != '\0'; cp++) {
    +
    983  if ('0' <= *cp && *cp <= '9') {
    +
    984  continue;
    +
    985  }
    +
    986 
    +
    987  if (*cp == '.') {
    +
    988  if (cp == op)
    +
    989  return 0; // dot can't be at the beginning.
    +
    990  if (dp != NULL)
    +
    991  return 0; // dot can't be appeared more than once.
    +
    992  dp = cp;
    +
    993  continue;
    +
    994  }
    +
    995 
    +
    996  return 0;
    +
    997  }
    +
    998 
    +
    999  if (cp == op) {
    +
    1000  return 0; // empty string
    +
    1001  }
    +
    1002 
    +
    1003  if (dp != NULL) {
    +
    1004  if (dp + 1 == cp)
    +
    1005  return 0; // dot can't be at the end.
    +
    1006  return 2; // float point
    +
    1007  }
    +
    1008 
    +
    1009  // integer
    +
    1010  return 1;
    +
    1011 }
    +
    1012 
    +
    1013 static int _is_str_bool(const char *s) {
    +
    1014  if (!strcasecmp(s, "true"))
    +
    1015  return 1;
    +
    1016  else if (!strcasecmp(s, "on"))
    +
    1017  return 1;
    +
    1018  else if (!strcasecmp(s, "yes"))
    +
    1019  return 1;
    +
    1020  else if (!strcasecmp(s, "1"))
    +
    1021  return 1;
    +
    1022  return 0;
    +
    1023 }
    +
    1024 
    +
    1025 #endif /* _DOXYGEN_SKIP */
    +
    1026 
    +
    1027 #endif /* DISABLE_QACONF */
    +
    1028 
    +
    static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags)
    qaconf_t->parse(): Run parser.
    Definition: qaconf.c:534
    +
    static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback)
    Set default callback function.
    Definition: qaconf.c:470
    +
    static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options)
    qaconf_t->addoptions(): Register option directives.
    Definition: qaconf.c:438
    +
    static const char * errmsg(qaconf_t *qaconf)
    qaconf_t->errmsg(): Get last error message.
    Definition: qaconf.c:572
    +
    static void setuserdata(qaconf_t *qaconf, const void *userdata)
    qaconf_t->setuserdata(): Set userdata which will be provided on callback.
    Definition: qaconf.c:506
    +
    static void reseterror(qaconf_t *qaconf)
    qaconf_t->reseterror(): Clear error message.
    Definition: qaconf.c:589
    +
    static void free_(qaconf_t *qaconf)
    qaconf_t->free(): Release resources.
    Definition: qaconf.c:605
    +
    qaconf_t * qaconf(void)
    Create a new configuration object.
    Definition: qaconf.c:245
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    diff --git a/doc/html/qconfig_8c.html b/doc/html/qconfig_8c.html index 0fe6ae00..3b7c46f4 100644 --- a/doc/html/qconfig_8c.html +++ b/doc/html/qconfig_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qconfig.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qconfig.c File Reference
    +
    +
    qconfig.c File Reference
    @@ -71,50 +71,34 @@

    Go to the source code of this file.

    - - +

    +

    Macros

    #define _INCLUDE_DIRECTIVE   "@INCLUDE "
    +#define _INCLUDE_DIRECTIVE   "@INCLUDE "
     
    - - - - - - - + + + + + +

    +

    Functions

    qlisttbl_t * qconfig_parse_file (qlisttbl_t *tbl, const char *filepath, char sepchar)
     Load & parse configuration file.
     
    qlisttbl_t * qconfig_parse_str (qlisttbl_t *tbl, const char *str, char sepchar)
     Parse string.
     
    qlisttbl_t * qconfig_parse_file (qlisttbl_t *tbl, const char *filepath, char sepchar)
     Load & parse configuration file. More...
     
    qlisttbl_t * qconfig_parse_str (qlisttbl_t *tbl, const char *str, char sepchar)
     Parse string. More...
     

    Detailed Description

    INI-style configuration file parser.

    Definition in file qconfig.c.

    -

    Macro Definition Documentation

    - -

    ◆ _INCLUDE_DIRECTIVE

    - -
    -
    - - - - -
    #define _INCLUDE_DIRECTIVE   "@INCLUDE "
    -
    - -

    Definition at line 47 of file qconfig.c.

    - -
    -
    -

    Function Documentation

    - -

    ◆ qconfig_parse_file()

    +

    Function Documentation

    + +

    ◆ qconfig_parse_file()

    - + @@ -155,9 +139,9 @@

    @INCLUDE config.def => include 'config.def' file.
    prefix=/tmp => set static value. 'prefix' is the key for this entry.
    -
    log=${prefix}/log => get the value from previously defined key 'prefix'.
    -
    user=${%USER} => get environment variable.
    -
    host=${!/bin/hostname -s} => run external command and put it's output.
    +
    log=${prefix}/log => get the value from previously defined key 'prefix'.
    +
    user=${%USER} => get environment variable.
    +
    host=${!/bin/hostname -s} => run external command and put it's output.
    id=${user}@${host}
    # now entering into 'system' section.
    @@ -173,15 +157,15 @@

    # escape section. (go back to root)
    []
    rev=822
    -
    static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
    qhttpclient->put(): Uploads a file to the remote host using PUT method.
    -
    static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
    qhttpclient->get(): Downloads a file from the remote host using GET method.
    +
    static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
    qhttpclient->put(): Uploads a file to the remote host using PUT method.
    Definition: qhttpclient.c:926
    +
    static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
    qhttpclient->get(): Downloads a file from the remote host using GET method.
    Definition: qhttpclient.c:694
    # This is "config.def" file.
    prefix = /usr/local
    bin = ${prefix}/bin
    log = ${prefix}/log
    user = unknown
    host = unknown
    -
    qlisttbl_t *tbl = qconfig_parse_file(NULL, "config.conf", '=', true);
    +
    qlisttbl_t *tbl = qconfig_parse_file(NULL, "config.conf", '=', true);
    tbl->debug(tbl, stdout);
    [Output]
    @@ -198,20 +182,20 @@

    daemon.port=1234? (5)

    daemon.name=seungyoung.kim_eng22_linux_x86_64? (28)
    rev=822? (4)
    -
    qlisttbl_t * qconfig_parse_file(qlisttbl_t *tbl, const char *filepath, char sepchar)
    Load & parse configuration file.
    Definition qconfig.c:126
    +
    qlisttbl_t * qconfig_parse_file(qlisttbl_t *tbl, const char *filepath, char sepchar)
    Load & parse configuration file.
    Definition: qconfig.c:126

    Definition at line 126 of file qconfig.c.

    - -

    ◆ qconfig_parse_str()

    + +

    ◆ qconfig_parse_str()

    qlisttbl_t * qconfig_parse_file qlisttbl_t* qconfig_parse_file ( qlisttbl_t *  tbl,
    - + @@ -246,10 +230,10 @@

    Returns
    a pointer of qlisttbl_t in case of successful, otherwise(file not found) returns NULL
    -
    See also
    qconfig_parse_file
    +
    See also
    qconfig_parse_file
    qlisttbl_t *tbl;
    -
    tbl = qconfig_parse_str(NULL, "key = value\nhello = world", '=');
    -
    qlisttbl_t * qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar)
    Parse string.
    Definition qconfig.c:216
    +
    tbl = qconfig_parse_str(NULL, "key = value\nhello = world", '=');
    +
    qlisttbl_t * qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar)
    Parse string.
    Definition: qconfig.c:216

    Definition at line 216 of file qconfig.c.

    @@ -261,7 +245,7 @@

    diff --git a/doc/html/qconfig_8c.js b/doc/html/qconfig_8c.js index 6022cf6e..38c7941d 100644 --- a/doc/html/qconfig_8c.js +++ b/doc/html/qconfig_8c.js @@ -1,5 +1,6 @@ var qconfig_8c = [ - [ "qconfig_parse_file", "qconfig_8c.html#afcd4cc94bda4c854e3d26e5e358d5507", null ], - [ "qconfig_parse_str", "qconfig_8c.html#afe9f5bb5dd171682d5d27e51151eac93", null ] + [ "_INCLUDE_DIRECTIVE", "qconfig_8c.html#ab62078f483e0002c4cd7d8f993a674a1", null ], + [ "qconfig_parse_file", "qconfig_8c.html#a935668a20d0b3f5b6b84b46e9262bc0b", null ], + [ "qconfig_parse_str", "qconfig_8c.html#ae70b6b473d2fcb3f17bde60297007336", null ] ]; \ No newline at end of file diff --git a/doc/html/qconfig_8c_source.html b/doc/html/qconfig_8c_source.html index 2bb4229f..328339d7 100644 --- a/doc/html/qconfig_8c_source.html +++ b/doc/html/qconfig_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qconfig.c Source File @@ -20,8 +20,8 @@

    qlisttbl_t * qconfig_parse_str qlisttbl_t* qconfig_parse_str ( qlisttbl_t *  tbl,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,461 +52,462 @@
    -
    qconfig.c
    +
    +
    qconfig.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qconfig.c INI-style configuration file parser.
    -
    31 */
    -
    32
    -
    33#ifndef DISABLE_QCONFIG
    -
    34
    -
    35#include <stdio.h>
    -
    36#include <stdlib.h>
    -
    37#include <stdbool.h>
    -
    38#include <string.h>
    -
    39#include <limits.h>
    -
    40#include <errno.h>
    -
    41#include "qinternal.h"
    -
    42#include "utilities/qfile.h"
    -
    43#include "utilities/qstring.h"
    -
    44#include "utilities/qsystem.h"
    -
    45#include "extensions/qconfig.h"
    -
    46
    -
    47#define _INCLUDE_DIRECTIVE "@INCLUDE "
    -
    48
    -
    49#ifndef _DOXYGEN_SKIP
    -
    50#define _VAR '$'
    -
    51#define _VAR_OPEN '{'
    -
    52#define _VAR_CLOSE '}'
    -
    53#define _VAR_CMD '!'
    -
    54#define _VAR_ENV '%'
    -
    55
    -
    56/* internal functions */
    -
    57static char *_parsestr(qlisttbl_t *tbl, const char *str);
    -
    58#endif
    -
    59
    -
    60/**
    -
    61 * Load & parse configuration file
    -
    62 *
    -
    63 * @param tbl a pointer of qlisttbl_t. NULL will generate a new table.
    -
    64 * @param filepath configuration file path
    -
    65 * @param sepchar separater used in configuration file to divice key and value
    -
    66 *
    -
    67 * @return a pointer of qlisttbl_t in case of successful,
    -
    68 * otherwise(file not found) returns NULL
    -
    69 *
    -
    70 * @code
    -
    71 * # This is "config.conf" file.
    -
    72 * # A line which starts with # character is comment
    -
    73 *
    -
    74 * @INCLUDE config.def => include 'config.def' file.
    -
    75 *
    -
    76 * prefix=/tmp => set static value. 'prefix' is the key for this entry.
    -
    77 * log=${prefix}/log => get the value from previously defined key 'prefix'.
    -
    78 * user=${%USER} => get environment variable.
    -
    79 * host=${!/bin/hostname -s} => run external command and put it's output.
    -
    80 * id=${user}@${host}
    -
    81 *
    -
    82 * # now entering into 'system' section.
    -
    83 * [system] => a key 'system.' with value 'system' will be inserted.
    -
    84 * ostype=${%OSTYPE} => 'system.ostype' is the key for this entry.
    -
    85 * machtype=${%MACHTYPE} => 'system.machtype' is the key for this entry.
    -
    86 *
    -
    87 * # entering into 'daemon' section.
    -
    88 * [daemon]
    -
    89 * port=1234
    -
    90 * name=${user}_${host}_${system.ostype}_${system.machtype}
    -
    91 *
    -
    92 * # escape section. (go back to root)
    -
    93 * []
    -
    94 * rev=822
    -
    95 * @endcode
    -
    96 *
    -
    97 * @code
    -
    98 * # This is "config.def" file.
    -
    99 * prefix = /usr/local
    -
    100 * bin = ${prefix}/bin
    -
    101 * log = ${prefix}/log
    -
    102 * user = unknown
    -
    103 * host = unknown
    -
    104 * @endcode
    -
    105 *
    -
    106 * @code
    -
    107 * qlisttbl_t *tbl = qconfig_parse_file(NULL, "config.conf", '=', true);
    -
    108 * tbl->debug(tbl, stdout);
    -
    109 *
    -
    110 * [Output]
    -
    111 * bin=/usr/local/bin? (15)
    -
    112 * prefix=/tmp? (5)
    -
    113 * log=/tmp/log? (9)
    -
    114 * user=seungyoung.kim? (9)
    -
    115 * host=eng22? (6)
    -
    116 * id=seungyoung.kim@eng22? (15)
    -
    117 * system.=system? (7)
    -
    118 * system.ostype=linux? (6)
    -
    119 * system.machtype=x86_64? (7)
    -
    120 * daemon.=daemon? (7)
    -
    121 * daemon.port=1234? (5)
    -
    122 * daemon.name=seungyoung.kim_eng22_linux_x86_64? (28)
    -
    123 * rev=822? (4)
    -
    124 * @endcode
    -
    125 */
    -
    126qlisttbl_t *qconfig_parse_file(qlisttbl_t *tbl, const char *filepath,
    -
    127 char sepchar) {
    -
    128 char *str = qfile_load(filepath, NULL);
    -
    129 if (str == NULL)
    -
    130 return NULL;
    -
    131
    -
    132 // process include directive
    -
    133 char *strp = str;
    -
    134
    -
    135 while ((strp = strstr(strp, _INCLUDE_DIRECTIVE)) != NULL) {
    -
    136 if (strp == str || strp[-1] == '\n') {
    -
    137 char buf[PATH_MAX];
    -
    138
    -
    139 // parse filename
    -
    140 char *tmpp;
    -
    141 for (tmpp = strp + CONST_STRLEN(_INCLUDE_DIRECTIVE);
    -
    142 *tmpp != '\n' && *tmpp != '\0'; tmpp++)
    -
    143 ;
    -
    144 int len = tmpp - (strp + CONST_STRLEN(_INCLUDE_DIRECTIVE));
    -
    145 if (len >= sizeof(buf)) {
    -
    146 DEBUG("Can't process %s directive.", _INCLUDE_DIRECTIVE);
    -
    147 free(str);
    -
    148 return NULL;
    -
    149 }
    -
    150
    -
    151 strncpy(buf, strp + CONST_STRLEN(_INCLUDE_DIRECTIVE), len);
    -
    152 buf[len] = '\0';
    -
    153 qstrtrim(buf);
    -
    154
    -
    155 // get full file path
    -
    156 if (!(buf[0] == '/' || buf[0] == '\\')) {
    -
    157 char tmp[PATH_MAX];
    -
    158 char *dir = qfile_get_dir(filepath);
    -
    159 if (strlen(dir) + 1 + strlen(buf) >= sizeof(buf)) {
    -
    160 DEBUG("Can't process %s directive.", _INCLUDE_DIRECTIVE);
    -
    161 free(dir);
    -
    162 free(str);
    -
    163 return NULL;
    -
    164 }
    -
    165 snprintf(tmp, sizeof(tmp), "%s/%s", dir, buf);
    -
    166 free(dir);
    -
    167
    -
    168 strcpy(buf, tmp);
    -
    169 }
    -
    170
    -
    171 // read file
    -
    172 char *incdata;
    -
    173 if (strlen(buf) == 0 || (incdata = qfile_load(buf, NULL)) == NULL) {
    -
    174 DEBUG("Can't process '%s%s' directive.", _INCLUDE_DIRECTIVE,
    -
    175 buf);
    -
    176 free(str);
    -
    177 return NULL;
    -
    178 }
    -
    179
    -
    180 // replace
    -
    181 strncpy(buf, strp, CONST_STRLEN(_INCLUDE_DIRECTIVE) + len);
    -
    182 buf[CONST_STRLEN(_INCLUDE_DIRECTIVE) + len] = '\0';
    -
    183 strp = qstrreplace("sn", str, buf, incdata);
    -
    184 free(incdata);
    -
    185 free(str);
    -
    186 str = strp;
    -
    187 } else {
    -
    188 strp += CONST_STRLEN(_INCLUDE_DIRECTIVE);
    -
    189 }
    -
    190 }
    -
    191
    -
    192 // parse
    -
    193 tbl = qconfig_parse_str(tbl, str, sepchar);
    -
    194 free(str);
    -
    195
    -
    196 return tbl;
    -
    197}
    -
    198
    -
    199/**
    -
    200 * Parse string
    -
    201 *
    -
    202 * @param tbl a pointer of qlisttbl_t. NULL will generate a new table.
    -
    203 * @param str key, value pair strings
    -
    204 * @param sepchar separater used in configuration file to divice key and value
    -
    205 *
    -
    206 * @return a pointer of qlisttbl_t in case of successful,
    -
    207 * otherwise(file not found) returns NULL
    -
    208 *
    -
    209 * @see qconfig_parse_file
    -
    210 *
    -
    211 * @code
    -
    212 * qlisttbl_t *tbl;
    -
    213 * tbl = qconfig_parse_str(NULL, "key = value\nhello = world", '=');
    -
    214 * @endcode
    -
    215 */
    -
    216qlisttbl_t *qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar) {
    -
    217 if (str == NULL)
    -
    218 return NULL;
    -
    219
    -
    220 if (tbl == NULL) {
    -
    221 tbl = qlisttbl(0);
    -
    222 if (tbl == NULL)
    -
    223 return NULL;
    -
    224 }
    -
    225
    -
    226 char *section = NULL;
    -
    227 char *org, *buf, *offset;
    -
    228 for (org = buf = offset = strdup(str); *offset != '\0';) {
    -
    229 // get one line into buf
    -
    230 for (buf = offset; *offset != '\n' && *offset != '\0'; offset++)
    -
    231 ;
    -
    232 if (*offset != '\0') {
    -
    233 *offset = '\0';
    -
    234 offset++;
    -
    235 }
    -
    236 qstrtrim(buf);
    -
    237
    -
    238 // skip blank or comment line
    -
    239 if ((buf[0] == '#') || (buf[0] == '\0'))
    -
    240 continue;
    -
    241
    -
    242 // section header
    -
    243 if ((buf[0] == '[') && (buf[strlen(buf) - 1] == ']')) {
    -
    244 // extract section name
    -
    245 if (section != NULL)
    -
    246 free(section);
    -
    247 section = strdup(buf + 1);
    -
    248 section[strlen(section) - 1] = '\0';
    -
    249 qstrtrim(section);
    -
    250
    -
    251 // remove section if section name is empty. ex) []
    -
    252 if (section[0] == '\0') {
    -
    253 free(section);
    -
    254 section = NULL;
    -
    255 continue;
    -
    256 }
    -
    257
    -
    258 // in order to put 'section.=section'
    -
    259 sprintf(buf, "%c%s", sepchar, section);
    -
    260 }
    -
    261
    -
    262 // parse & store
    -
    263 char *value = strdup(buf);
    -
    264 char *name = _q_makeword(value, sepchar);
    -
    265 qstrtrim(value);
    -
    266 qstrtrim(name);
    -
    267
    -
    268 // put section name as a prefix
    -
    269 if (section != NULL) {
    -
    270 char *newname = qstrdupf("%s.%s", section, name);
    -
    271 free(name);
    -
    272 name = newname;
    -
    273 }
    -
    274
    -
    275 // get parsed string
    -
    276 char *newvalue = _parsestr(tbl, value);
    -
    277 if (newvalue != NULL) {
    -
    278 tbl->putstr(tbl, name, newvalue);
    -
    279 free(newvalue);
    -
    280 }
    -
    281
    -
    282 free(name);
    -
    283 free(value);
    -
    284 }
    -
    285 free(org);
    -
    286 if (section != NULL)
    -
    287 free(section);
    -
    288
    -
    289 return tbl;
    -
    290}
    -
    291
    -
    292#ifndef _DOXYGEN_SKIP
    -
    293
    -
    294/**
    -
    295 * (qlisttbl_t*)->parsestr(): Parse a string and replace variables in the
    -
    296 * string to the data in this list.
    -
    297 *
    -
    298 * @param tbl qlisttbl container pointer.
    -
    299 * @param str string value which may contain variables like ${...}
    -
    300 *
    -
    301 * @return malloced string if successful, otherwise returns NULL.
    -
    302 * @retval errno will be set in error condition.
    -
    303 * - EINVAL : Invalid argument.
    -
    304 *
    -
    305 * @code
    -
    306 * ${key_name} - replace this with a matched value data in this list.
    -
    307 * ${!system_command} - run external command and put it's output here.
    -
    308 * ${%PATH} - get environment variable.
    -
    309 * @endcode
    -
    310 *
    -
    311 * @code
    -
    312 * --[tbl Table]------------------------
    -
    313 * NAME = qLibc
    -
    314 * -------------------------------------
    -
    315 *
    -
    316 * char *str = _parsestr(tbl, "${NAME}, ${%HOME}, ${!date -u}");
    -
    317 * if(str != NULL) {
    -
    318 * printf("%s\n", str);
    -
    319 * free(str);
    -
    320 * }
    -
    321 *
    -
    322 * [Output]
    -
    323 * qLibc, /home/qlibc, Wed Nov 24 00:30:58 UTC 2010
    -
    324 * @endcode
    -
    325 */
    -
    326static char *_parsestr(qlisttbl_t *tbl, const char *str) {
    -
    327 if (str == NULL) {
    -
    328 errno = EINVAL;
    -
    329 return NULL;
    -
    330 }
    -
    331
    -
    332 bool loop;
    -
    333 char *value = strdup(str);
    -
    334 do {
    -
    335 loop = false;
    -
    336
    -
    337 // find ${
    -
    338 char *s, *e;
    -
    339 int openedbrakets;
    -
    340 for (s = value; *s != '\0'; s++) {
    -
    341 if (!(*s == _VAR && *(s + 1) == _VAR_OPEN))
    -
    342 continue;
    -
    343
    -
    344 // found ${, try to find }. s points $
    -
    345 openedbrakets = 1; // braket open counter
    -
    346 for (e = s + 2; *e != '\0'; e++) {
    -
    347 if (*e == _VAR && *(e + 1) == _VAR_OPEN) { // found internal ${
    -
    348 // e is always bigger than s, negative overflow never occure
    -
    349 s = e - 1;
    -
    350 break;
    -
    351 } else if (*e == _VAR_OPEN)
    -
    352 openedbrakets++;
    -
    353 else if (*e == _VAR_CLOSE)
    -
    354 openedbrakets--;
    -
    355 else
    -
    356 continue;
    -
    357
    -
    358 if (openedbrakets == 0)
    -
    359 break;
    -
    360 }
    -
    361 if (*e == '\0')
    -
    362 break; // braket mismatch
    -
    363 if (openedbrakets > 0)
    -
    364 continue; // found internal ${
    -
    365
    -
    366 // pick string between ${, }
    -
    367 int varlen = e - s - 2; // length between ${ , }
    -
    368 char *varstr = (char *) malloc(varlen + 3 + 1);
    -
    369 if (varstr == NULL)
    -
    370 continue;
    -
    371 strncpy(varstr, s + 2, varlen);
    -
    372 varstr[varlen] = '\0';
    -
    373
    -
    374 // get the new string to replace
    -
    375 char *newstr = NULL;
    -
    376 switch (varstr[0]) {
    -
    377 case _VAR_CMD: {
    -
    378 if (varlen - 1 == 0) {
    -
    379 newstr = strdup("");
    -
    380 break;
    -
    381 }
    -
    382 if ((newstr = qstrtrim(qsyscmd(varstr + 1))) == NULL) {
    -
    383 newstr = strdup("");
    -
    384 }
    -
    385 break;
    -
    386 }
    -
    387 case _VAR_ENV: {
    -
    388 if (varlen - 1 == 0) {
    -
    389 newstr = strdup("");
    -
    390 break;
    -
    391 }
    -
    392 newstr = strdup(qgetenv(varstr + 1, ""));
    -
    393 break;
    -
    394 }
    -
    395 default: {
    -
    396 if (varlen == 0) {
    -
    397 newstr = strdup("");
    -
    398 break;
    -
    399 }
    -
    400 if ((newstr = tbl->getstr(tbl, varstr, true)) == NULL) {
    -
    401 s = e; // not found
    -
    402 continue;
    -
    403 }
    -
    404 break;
    -
    405 }
    -
    406 }
    -
    407
    -
    408 // replace
    -
    409 strncpy(varstr, s, varlen + 3); // ${str}
    -
    410 varstr[varlen + 3] = '\0';
    -
    411
    -
    412 s = qstrreplace("sn", value, varstr, newstr);
    -
    413 free(newstr);
    -
    414 free(varstr);
    -
    415 free(value);
    -
    416 value = s;
    -
    417
    -
    418 loop = true;
    -
    419 break;
    -
    420 }
    -
    421 } while (loop == true);
    -
    422
    -
    423 return value;
    -
    424}
    -
    425
    -
    426#endif /* _DOXYGEN_SKIP */
    -
    427
    -
    428#endif /* DISABLE_QCONFIG */
    -
    429
    -
    qlisttbl_t * qconfig_parse_file(qlisttbl_t *tbl, const char *filepath, char sepchar)
    Load & parse configuration file.
    Definition qconfig.c:126
    -
    qlisttbl_t * qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar)
    Parse string.
    Definition qconfig.c:216
    -
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition qfile.c:159
    -
    char * qfile_get_dir(const char *filepath)
    Get directory suffix from filepath.
    Definition qfile.c:371
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    -
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition qstring.c:363
    -
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition qstring.c:236
    -
    const char * qgetenv(const char *envname, const char *defstr)
    Get system environment variable.
    Definition qsystem.c:54
    -
    char * qsyscmd(const char *cmd)
    Get the result string of external command execution.
    Definition qsystem.c:71
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qconfig.c INI-style configuration file parser.
    +
    31  */
    +
    32 
    +
    33 #ifndef DISABLE_QCONFIG
    +
    34 
    +
    35 #include <stdio.h>
    +
    36 #include <stdlib.h>
    +
    37 #include <stdbool.h>
    +
    38 #include <string.h>
    +
    39 #include <limits.h>
    +
    40 #include <errno.h>
    +
    41 #include "qinternal.h"
    +
    42 #include "utilities/qfile.h"
    +
    43 #include "utilities/qstring.h"
    +
    44 #include "utilities/qsystem.h"
    +
    45 #include "extensions/qconfig.h"
    +
    46 
    +
    47 #define _INCLUDE_DIRECTIVE "@INCLUDE "
    +
    48 
    +
    49 #ifndef _DOXYGEN_SKIP
    +
    50 #define _VAR '$'
    +
    51 #define _VAR_OPEN '{'
    +
    52 #define _VAR_CLOSE '}'
    +
    53 #define _VAR_CMD '!'
    +
    54 #define _VAR_ENV '%'
    +
    55 
    +
    56 /* internal functions */
    +
    57 static char *_parsestr(qlisttbl_t *tbl, const char *str);
    +
    58 #endif
    +
    59 
    +
    60 /**
    +
    61  * Load & parse configuration file
    +
    62  *
    +
    63  * @param tbl a pointer of qlisttbl_t. NULL will generate a new table.
    +
    64  * @param filepath configuration file path
    +
    65  * @param sepchar separater used in configuration file to divice key and value
    +
    66  *
    +
    67  * @return a pointer of qlisttbl_t in case of successful,
    +
    68  * otherwise(file not found) returns NULL
    +
    69  *
    +
    70  * @code
    +
    71  * # This is "config.conf" file.
    +
    72  * # A line which starts with # character is comment
    +
    73  *
    +
    74  * @INCLUDE config.def => include 'config.def' file.
    +
    75  *
    +
    76  * prefix=/tmp => set static value. 'prefix' is the key for this entry.
    +
    77  * log=${prefix}/log => get the value from previously defined key 'prefix'.
    +
    78  * user=${%USER} => get environment variable.
    +
    79  * host=${!/bin/hostname -s} => run external command and put it's output.
    +
    80  * id=${user}@${host}
    +
    81  *
    +
    82  * # now entering into 'system' section.
    +
    83  * [system] => a key 'system.' with value 'system' will be inserted.
    +
    84  * ostype=${%OSTYPE} => 'system.ostype' is the key for this entry.
    +
    85  * machtype=${%MACHTYPE} => 'system.machtype' is the key for this entry.
    +
    86  *
    +
    87  * # entering into 'daemon' section.
    +
    88  * [daemon]
    +
    89  * port=1234
    +
    90  * name=${user}_${host}_${system.ostype}_${system.machtype}
    +
    91  *
    +
    92  * # escape section. (go back to root)
    +
    93  * []
    +
    94  * rev=822
    +
    95  * @endcode
    +
    96  *
    +
    97  * @code
    +
    98  * # This is "config.def" file.
    +
    99  * prefix = /usr/local
    +
    100  * bin = ${prefix}/bin
    +
    101  * log = ${prefix}/log
    +
    102  * user = unknown
    +
    103  * host = unknown
    +
    104  * @endcode
    +
    105  *
    +
    106  * @code
    +
    107  * qlisttbl_t *tbl = qconfig_parse_file(NULL, "config.conf", '=', true);
    +
    108  * tbl->debug(tbl, stdout);
    +
    109  *
    +
    110  * [Output]
    +
    111  * bin=/usr/local/bin? (15)
    +
    112  * prefix=/tmp? (5)
    +
    113  * log=/tmp/log? (9)
    +
    114  * user=seungyoung.kim? (9)
    +
    115  * host=eng22? (6)
    +
    116  * id=seungyoung.kim@eng22? (15)
    +
    117  * system.=system? (7)
    +
    118  * system.ostype=linux? (6)
    +
    119  * system.machtype=x86_64? (7)
    +
    120  * daemon.=daemon? (7)
    +
    121  * daemon.port=1234? (5)
    +
    122  * daemon.name=seungyoung.kim_eng22_linux_x86_64? (28)
    +
    123  * rev=822? (4)
    +
    124  * @endcode
    +
    125  */
    +
    126 qlisttbl_t *qconfig_parse_file(qlisttbl_t *tbl, const char *filepath,
    +
    127  char sepchar) {
    +
    128  char *str = qfile_load(filepath, NULL);
    +
    129  if (str == NULL)
    +
    130  return NULL;
    +
    131 
    +
    132  // process include directive
    +
    133  char *strp = str;
    +
    134 
    +
    135  while ((strp = strstr(strp, _INCLUDE_DIRECTIVE)) != NULL) {
    +
    136  if (strp == str || strp[-1] == '\n') {
    +
    137  char buf[PATH_MAX];
    +
    138 
    +
    139  // parse filename
    +
    140  char *tmpp;
    +
    141  for (tmpp = strp + CONST_STRLEN(_INCLUDE_DIRECTIVE);
    +
    142  *tmpp != '\n' && *tmpp != '\0'; tmpp++)
    +
    143  ;
    +
    144  int len = tmpp - (strp + CONST_STRLEN(_INCLUDE_DIRECTIVE));
    +
    145  if (len >= sizeof(buf)) {
    +
    146  DEBUG("Can't process %s directive.", _INCLUDE_DIRECTIVE);
    +
    147  free(str);
    +
    148  return NULL;
    +
    149  }
    +
    150 
    +
    151  strncpy(buf, strp + CONST_STRLEN(_INCLUDE_DIRECTIVE), len);
    +
    152  buf[len] = '\0';
    +
    153  qstrtrim(buf);
    +
    154 
    +
    155  // get full file path
    +
    156  if (!(buf[0] == '/' || buf[0] == '\\')) {
    +
    157  char tmp[PATH_MAX];
    +
    158  char *dir = qfile_get_dir(filepath);
    +
    159  if (strlen(dir) + 1 + strlen(buf) >= sizeof(buf)) {
    +
    160  DEBUG("Can't process %s directive.", _INCLUDE_DIRECTIVE);
    +
    161  free(dir);
    +
    162  free(str);
    +
    163  return NULL;
    +
    164  }
    +
    165  snprintf(tmp, sizeof(tmp), "%s/%s", dir, buf);
    +
    166  free(dir);
    +
    167 
    +
    168  strcpy(buf, tmp);
    +
    169  }
    +
    170 
    +
    171  // read file
    +
    172  char *incdata;
    +
    173  if (strlen(buf) == 0 || (incdata = qfile_load(buf, NULL)) == NULL) {
    +
    174  DEBUG("Can't process '%s%s' directive.", _INCLUDE_DIRECTIVE,
    +
    175  buf);
    +
    176  free(str);
    +
    177  return NULL;
    +
    178  }
    +
    179 
    +
    180  // replace
    +
    181  strncpy(buf, strp, CONST_STRLEN(_INCLUDE_DIRECTIVE) + len);
    +
    182  buf[CONST_STRLEN(_INCLUDE_DIRECTIVE) + len] = '\0';
    +
    183  strp = qstrreplace("sn", str, buf, incdata);
    +
    184  free(incdata);
    +
    185  free(str);
    +
    186  str = strp;
    +
    187  } else {
    +
    188  strp += CONST_STRLEN(_INCLUDE_DIRECTIVE);
    +
    189  }
    +
    190  }
    +
    191 
    +
    192  // parse
    +
    193  tbl = qconfig_parse_str(tbl, str, sepchar);
    +
    194  free(str);
    +
    195 
    +
    196  return tbl;
    +
    197 }
    +
    198 
    +
    199 /**
    +
    200  * Parse string
    +
    201  *
    +
    202  * @param tbl a pointer of qlisttbl_t. NULL will generate a new table.
    +
    203  * @param str key, value pair strings
    +
    204  * @param sepchar separater used in configuration file to divice key and value
    +
    205  *
    +
    206  * @return a pointer of qlisttbl_t in case of successful,
    +
    207  * otherwise(file not found) returns NULL
    +
    208  *
    +
    209  * @see qconfig_parse_file
    +
    210  *
    +
    211  * @code
    +
    212  * qlisttbl_t *tbl;
    +
    213  * tbl = qconfig_parse_str(NULL, "key = value\nhello = world", '=');
    +
    214  * @endcode
    +
    215  */
    +
    216 qlisttbl_t *qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar) {
    +
    217  if (str == NULL)
    +
    218  return NULL;
    +
    219 
    +
    220  if (tbl == NULL) {
    +
    221  tbl = qlisttbl(0);
    +
    222  if (tbl == NULL)
    +
    223  return NULL;
    +
    224  }
    +
    225 
    +
    226  char *section = NULL;
    +
    227  char *org, *buf, *offset;
    +
    228  for (org = buf = offset = strdup(str); *offset != '\0';) {
    +
    229  // get one line into buf
    +
    230  for (buf = offset; *offset != '\n' && *offset != '\0'; offset++)
    +
    231  ;
    +
    232  if (*offset != '\0') {
    +
    233  *offset = '\0';
    +
    234  offset++;
    +
    235  }
    +
    236  qstrtrim(buf);
    +
    237 
    +
    238  // skip blank or comment line
    +
    239  if ((buf[0] == '#') || (buf[0] == '\0'))
    +
    240  continue;
    +
    241 
    +
    242  // section header
    +
    243  if ((buf[0] == '[') && (buf[strlen(buf) - 1] == ']')) {
    +
    244  // extract section name
    +
    245  if (section != NULL)
    +
    246  free(section);
    +
    247  section = strdup(buf + 1);
    +
    248  section[strlen(section) - 1] = '\0';
    +
    249  qstrtrim(section);
    +
    250 
    +
    251  // remove section if section name is empty. ex) []
    +
    252  if (section[0] == '\0') {
    +
    253  free(section);
    +
    254  section = NULL;
    +
    255  continue;
    +
    256  }
    +
    257 
    +
    258  // in order to put 'section.=section'
    +
    259  sprintf(buf, "%c%s", sepchar, section);
    +
    260  }
    +
    261 
    +
    262  // parse & store
    +
    263  char *value = strdup(buf);
    +
    264  char *name = _q_makeword(value, sepchar);
    +
    265  qstrtrim(value);
    +
    266  qstrtrim(name);
    +
    267 
    +
    268  // put section name as a prefix
    +
    269  if (section != NULL) {
    +
    270  char *newname = qstrdupf("%s.%s", section, name);
    +
    271  free(name);
    +
    272  name = newname;
    +
    273  }
    +
    274 
    +
    275  // get parsed string
    +
    276  char *newvalue = _parsestr(tbl, value);
    +
    277  if (newvalue != NULL) {
    +
    278  tbl->putstr(tbl, name, newvalue);
    +
    279  free(newvalue);
    +
    280  }
    +
    281 
    +
    282  free(name);
    +
    283  free(value);
    +
    284  }
    +
    285  free(org);
    +
    286  if (section != NULL)
    +
    287  free(section);
    +
    288 
    +
    289  return tbl;
    +
    290 }
    +
    291 
    +
    292 #ifndef _DOXYGEN_SKIP
    +
    293 
    +
    294 /**
    +
    295  * (qlisttbl_t*)->parsestr(): Parse a string and replace variables in the
    +
    296  * string to the data in this list.
    +
    297  *
    +
    298  * @param tbl qlisttbl container pointer.
    +
    299  * @param str string value which may contain variables like ${...}
    +
    300  *
    +
    301  * @return malloced string if successful, otherwise returns NULL.
    +
    302  * @retval errno will be set in error condition.
    +
    303  * - EINVAL : Invalid argument.
    +
    304  *
    +
    305  * @code
    +
    306  * ${key_name} - replace this with a matched value data in this list.
    +
    307  * ${!system_command} - run external command and put it's output here.
    +
    308  * ${%PATH} - get environment variable.
    +
    309  * @endcode
    +
    310  *
    +
    311  * @code
    +
    312  * --[tbl Table]------------------------
    +
    313  * NAME = qLibc
    +
    314  * -------------------------------------
    +
    315  *
    +
    316  * char *str = _parsestr(tbl, "${NAME}, ${%HOME}, ${!date -u}");
    +
    317  * if(str != NULL) {
    +
    318  * printf("%s\n", str);
    +
    319  * free(str);
    +
    320  * }
    +
    321  *
    +
    322  * [Output]
    +
    323  * qLibc, /home/qlibc, Wed Nov 24 00:30:58 UTC 2010
    +
    324  * @endcode
    +
    325  */
    +
    326 static char *_parsestr(qlisttbl_t *tbl, const char *str) {
    +
    327  if (str == NULL) {
    +
    328  errno = EINVAL;
    +
    329  return NULL;
    +
    330  }
    +
    331 
    +
    332  bool loop;
    +
    333  char *value = strdup(str);
    +
    334  do {
    +
    335  loop = false;
    +
    336 
    +
    337  // find ${
    +
    338  char *s, *e;
    +
    339  int openedbrakets;
    +
    340  for (s = value; *s != '\0'; s++) {
    +
    341  if (!(*s == _VAR && *(s + 1) == _VAR_OPEN))
    +
    342  continue;
    +
    343 
    +
    344  // found ${, try to find }. s points $
    +
    345  openedbrakets = 1; // braket open counter
    +
    346  for (e = s + 2; *e != '\0'; e++) {
    +
    347  if (*e == _VAR && *(e + 1) == _VAR_OPEN) { // found internal ${
    +
    348  // e is always bigger than s, negative overflow never occure
    +
    349  s = e - 1;
    +
    350  break;
    +
    351  } else if (*e == _VAR_OPEN)
    +
    352  openedbrakets++;
    +
    353  else if (*e == _VAR_CLOSE)
    +
    354  openedbrakets--;
    +
    355  else
    +
    356  continue;
    +
    357 
    +
    358  if (openedbrakets == 0)
    +
    359  break;
    +
    360  }
    +
    361  if (*e == '\0')
    +
    362  break; // braket mismatch
    +
    363  if (openedbrakets > 0)
    +
    364  continue; // found internal ${
    +
    365 
    +
    366  // pick string between ${, }
    +
    367  int varlen = e - s - 2; // length between ${ , }
    +
    368  char *varstr = (char *) malloc(varlen + 3 + 1);
    +
    369  if (varstr == NULL)
    +
    370  continue;
    +
    371  strncpy(varstr, s + 2, varlen);
    +
    372  varstr[varlen] = '\0';
    +
    373 
    +
    374  // get the new string to replace
    +
    375  char *newstr = NULL;
    +
    376  switch (varstr[0]) {
    +
    377  case _VAR_CMD: {
    +
    378  if (varlen - 1 == 0) {
    +
    379  newstr = strdup("");
    +
    380  break;
    +
    381  }
    +
    382  if ((newstr = qstrtrim(qsyscmd(varstr + 1))) == NULL) {
    +
    383  newstr = strdup("");
    +
    384  }
    +
    385  break;
    +
    386  }
    +
    387  case _VAR_ENV: {
    +
    388  if (varlen - 1 == 0) {
    +
    389  newstr = strdup("");
    +
    390  break;
    +
    391  }
    +
    392  newstr = strdup(qgetenv(varstr + 1, ""));
    +
    393  break;
    +
    394  }
    +
    395  default: {
    +
    396  if (varlen == 0) {
    +
    397  newstr = strdup("");
    +
    398  break;
    +
    399  }
    +
    400  if ((newstr = tbl->getstr(tbl, varstr, true)) == NULL) {
    +
    401  s = e; // not found
    +
    402  continue;
    +
    403  }
    +
    404  break;
    +
    405  }
    +
    406  }
    +
    407 
    +
    408  // replace
    +
    409  strncpy(varstr, s, varlen + 3); // ${str}
    +
    410  varstr[varlen + 3] = '\0';
    +
    411 
    +
    412  s = qstrreplace("sn", value, varstr, newstr);
    +
    413  free(newstr);
    +
    414  free(varstr);
    +
    415  free(value);
    +
    416  value = s;
    +
    417 
    +
    418  loop = true;
    +
    419  break;
    +
    420  }
    +
    421  } while (loop == true);
    +
    422 
    +
    423  return value;
    +
    424 }
    +
    425 
    +
    426 #endif /* _DOXYGEN_SKIP */
    +
    427 
    +
    428 #endif /* DISABLE_QCONFIG */
    +
    429 
    +
    qlisttbl_t * qconfig_parse_file(qlisttbl_t *tbl, const char *filepath, char sepchar)
    Load & parse configuration file.
    Definition: qconfig.c:126
    +
    qlisttbl_t * qconfig_parse_str(qlisttbl_t *tbl, const char *str, char sepchar)
    Parse string.
    Definition: qconfig.c:216
    +
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition: qfile.c:159
    +
    char * qfile_get_dir(const char *filepath)
    Get directory suffix from filepath.
    Definition: qfile.c:371
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150
    +
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition: qstring.c:236
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    +
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition: qstring.c:363
    +
    char * qsyscmd(const char *cmd)
    Get the result string of external command execution.
    Definition: qsystem.c:71
    +
    const char * qgetenv(const char *envname, const char *defstr)
    Get system environment variable.
    Definition: qsystem.c:54
    diff --git a/doc/html/qcount_8c.html b/doc/html/qcount_8c.html index 59d2a2e5..3332e5bc 100644 --- a/doc/html/qcount_8c.html +++ b/doc/html/qcount_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qcount.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qcount.c File Reference
    +
    +
    qcount.c File Reference
    @@ -70,16 +70,16 @@

    Go to the source code of this file.

    - - + - + - +

    +

    Functions

    int64_t qcount_read (const char *filepath)
     Read counter(integer) from file with advisory file locking.
     Read counter(integer) from file with advisory file locking. More...
     
    bool qcount_save (const char *filepath, int64_t number)
     Save counter(integer) to file with advisory file locking.
     Save counter(integer) to file with advisory file locking. More...
     
    int64_t qcount_update (const char *filepath, int64_t number)
     Increases(or decrease) the counter value as much as specified number with advisory file locking.
     Increases(or decrease) the counter value as much as specified number with advisory file locking. More...
     

    Detailed Description

    @@ -87,8 +87,8 @@

    Definition in file qcount.c.

    Function Documentation

    - -

    ◆ qcount_read()

    + +

    ◆ qcount_read()

    @@ -111,10 +111,10 @@

    Returns
    counter value readed from file. in case of failure, returns 0.
    -
    qcount_save("number.dat", 75);
    -
    int count = qcount_read("number.dat");
    -
    bool qcount_save(const char *filepath, int64_t number)
    Save counter(integer) to file with advisory file locking.
    Definition qcount.c:95
    -
    int64_t qcount_read(const char *filepath)
    Read counter(integer) from file with advisory file locking.
    Definition qcount.c:65
    +
    qcount_save("number.dat", 75);
    +
    int count = qcount_read("number.dat");
    +
    bool qcount_save(const char *filepath, int64_t number)
    Save counter(integer) to file with advisory file locking.
    Definition: qcount.c:95
    +
    int64_t qcount_read(const char *filepath)
    Read counter(integer) from file with advisory file locking.
    Definition: qcount.c:65
    ---- number.dat ----
    75
    --------------------
    @@ -123,8 +123,8 @@

    -

    ◆ qcount_save()

    + +

    ◆ qcount_save()

    @@ -158,14 +158,14 @@

    Returns
    true if successful, otherwise returns false.
    -
    qcount_save("number.dat", 75);
    +
    qcount_save("number.dat", 75);

    Definition at line 95 of file qcount.c.

    - -

    ◆ qcount_update()

    + +

    ◆ qcount_update()

    @@ -200,8 +200,8 @@

    Returns
    updated counter value. in case of failure, returns 0.
    int count;
    -
    count = qcount_update("number.dat", -3);
    -
    int64_t qcount_update(const char *filepath, int64_t number)
    Increases(or decrease) the counter value as much as specified number with advisory file locking.
    Definition qcount.c:124
    +
    count = qcount_update("number.dat", -3);
    +
    int64_t qcount_update(const char *filepath, int64_t number)
    Increases(or decrease) the counter value as much as specified number with advisory file locking.
    Definition: qcount.c:124

    Definition at line 124 of file qcount.c.

    @@ -213,7 +213,7 @@

    diff --git a/doc/html/qcount_8c_source.html b/doc/html/qcount_8c_source.html index 24ee0426..55cd05be 100644 --- a/doc/html/qcount_8c_source.html +++ b/doc/html/qcount_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qcount.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,157 +52,158 @@
    -
    qcount.c
    +
    +
    qcount.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qcount.c Counter file handling APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <stdint.h>
    -
    37#include <inttypes.h>
    -
    38#include <string.h>
    -
    39#include <fcntl.h>
    -
    40#include <unistd.h>
    -
    41#include <sys/types.h>
    -
    42#include <sys/stat.h>
    -
    43#include "qinternal.h"
    -
    44#include "utilities/qstring.h"
    -
    45#include "utilities/qcount.h"
    -
    46
    -
    47/**
    -
    48 * Read counter(integer) from file with advisory file locking.
    -
    49 *
    -
    50 * @param filepath file path
    -
    51 *
    -
    52 * @return counter value readed from file. in case of failure, returns 0.
    -
    53 *
    -
    54 * @code
    -
    55 * qcount_save("number.dat", 75);
    -
    56 * int count = qcount_read("number.dat");
    -
    57 * @endcode
    -
    58 *
    -
    59 * @code
    -
    60 * ---- number.dat ----
    -
    61 * 75
    -
    62 * --------------------
    -
    63 * @endcode
    -
    64 */
    -
    65int64_t qcount_read(const char *filepath) {
    -
    66 int fd = open(filepath, O_RDONLY, 0);
    -
    67 if (fd < 0)
    -
    68 return 0;
    -
    69
    -
    70 char buf[20 + 1];
    -
    71 ssize_t readed = read(fd, buf, (sizeof(buf) - 1));
    -
    72 close(fd);
    -
    73
    -
    74 int64_t num = 0;
    -
    75 if (readed > 0) {
    -
    76 buf[readed] = '\0';
    -
    77 num = atoll(buf);
    -
    78 }
    -
    79
    -
    80 return num;
    -
    81}
    -
    82
    -
    83/**
    -
    84 * Save counter(integer) to file with advisory file locking.
    -
    85 *
    -
    86 * @param filepath file path
    -
    87 * @param number counter integer value
    -
    88 *
    -
    89 * @return true if successful, otherwise returns false.
    -
    90 *
    -
    91 * @code
    -
    92 * qcount_save("number.dat", 75);
    -
    93 * @endcode
    -
    94 */
    -
    95bool qcount_save(const char *filepath, int64_t number) {
    -
    96 int fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC,
    -
    97 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    -
    98 if (fd < 0)
    -
    99 return false;
    -
    100
    -
    101 char *str = qstrdupf("%"PRId64, number);
    -
    102 ssize_t updated = write(fd, str, strlen(str));
    -
    103 close(fd);
    -
    104
    -
    105 if (updated > 0)
    -
    106 return true;
    -
    107 return false;
    -
    108}
    -
    109
    -
    110/**
    -
    111 * Increases(or decrease) the counter value as much as specified number
    -
    112 * with advisory file locking.
    -
    113 *
    -
    114 * @param filepath file path
    -
    115 * @param number how much increase or decrease
    -
    116 *
    -
    117 * @return updated counter value. in case of failure, returns 0.
    -
    118 *
    -
    119 * @code
    -
    120 * int count;
    -
    121 * count = qcount_update("number.dat", -3);
    -
    122 * @endcode
    -
    123 */
    -
    124int64_t qcount_update(const char *filepath, int64_t number) {
    -
    125 int64_t counter = qcount_read(filepath);
    -
    126 counter += number;
    -
    127 if (qcount_save(filepath, counter) == true) {
    -
    128 return counter;
    -
    129 }
    -
    130 return 0;
    -
    131}
    -
    bool qcount_save(const char *filepath, int64_t number)
    Save counter(integer) to file with advisory file locking.
    Definition qcount.c:95
    -
    int64_t qcount_read(const char *filepath)
    Read counter(integer) from file with advisory file locking.
    Definition qcount.c:65
    -
    int64_t qcount_update(const char *filepath, int64_t number)
    Increases(or decrease) the counter value as much as specified number with advisory file locking.
    Definition qcount.c:124
    -
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition qstring.c:363
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qcount.c Counter file handling APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <stdint.h>
    +
    37 #include <inttypes.h>
    +
    38 #include <string.h>
    +
    39 #include <fcntl.h>
    +
    40 #include <unistd.h>
    +
    41 #include <sys/types.h>
    +
    42 #include <sys/stat.h>
    +
    43 #include "qinternal.h"
    +
    44 #include "utilities/qstring.h"
    +
    45 #include "utilities/qcount.h"
    +
    46 
    +
    47 /**
    +
    48  * Read counter(integer) from file with advisory file locking.
    +
    49  *
    +
    50  * @param filepath file path
    +
    51  *
    +
    52  * @return counter value readed from file. in case of failure, returns 0.
    +
    53  *
    +
    54  * @code
    +
    55  * qcount_save("number.dat", 75);
    +
    56  * int count = qcount_read("number.dat");
    +
    57  * @endcode
    +
    58  *
    +
    59  * @code
    +
    60  * ---- number.dat ----
    +
    61  * 75
    +
    62  * --------------------
    +
    63  * @endcode
    +
    64  */
    +
    65 int64_t qcount_read(const char *filepath) {
    +
    66  int fd = open(filepath, O_RDONLY, 0);
    +
    67  if (fd < 0)
    +
    68  return 0;
    +
    69 
    +
    70  char buf[20 + 1];
    +
    71  ssize_t readed = read(fd, buf, (sizeof(buf) - 1));
    +
    72  close(fd);
    +
    73 
    +
    74  int64_t num = 0;
    +
    75  if (readed > 0) {
    +
    76  buf[readed] = '\0';
    +
    77  num = atoll(buf);
    +
    78  }
    +
    79 
    +
    80  return num;
    +
    81 }
    +
    82 
    +
    83 /**
    +
    84  * Save counter(integer) to file with advisory file locking.
    +
    85  *
    +
    86  * @param filepath file path
    +
    87  * @param number counter integer value
    +
    88  *
    +
    89  * @return true if successful, otherwise returns false.
    +
    90  *
    +
    91  * @code
    +
    92  * qcount_save("number.dat", 75);
    +
    93  * @endcode
    +
    94  */
    +
    95 bool qcount_save(const char *filepath, int64_t number) {
    +
    96  int fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC,
    +
    97  (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    +
    98  if (fd < 0)
    +
    99  return false;
    +
    100 
    +
    101  char *str = qstrdupf("%"PRId64, number);
    +
    102  ssize_t updated = write(fd, str, strlen(str));
    +
    103  close(fd);
    +
    104 
    +
    105  if (updated > 0)
    +
    106  return true;
    +
    107  return false;
    +
    108 }
    +
    109 
    +
    110 /**
    +
    111  * Increases(or decrease) the counter value as much as specified number
    +
    112  * with advisory file locking.
    +
    113  *
    +
    114  * @param filepath file path
    +
    115  * @param number how much increase or decrease
    +
    116  *
    +
    117  * @return updated counter value. in case of failure, returns 0.
    +
    118  *
    +
    119  * @code
    +
    120  * int count;
    +
    121  * count = qcount_update("number.dat", -3);
    +
    122  * @endcode
    +
    123  */
    +
    124 int64_t qcount_update(const char *filepath, int64_t number) {
    +
    125  int64_t counter = qcount_read(filepath);
    +
    126  counter += number;
    +
    127  if (qcount_save(filepath, counter) == true) {
    +
    128  return counter;
    +
    129  }
    +
    130  return 0;
    +
    131 }
    +
    bool qcount_save(const char *filepath, int64_t number)
    Save counter(integer) to file with advisory file locking.
    Definition: qcount.c:95
    +
    int64_t qcount_read(const char *filepath)
    Read counter(integer) from file with advisory file locking.
    Definition: qcount.c:65
    +
    int64_t qcount_update(const char *filepath, int64_t number)
    Increases(or decrease) the counter value as much as specified number with advisory file locking.
    Definition: qcount.c:124
    +
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition: qstring.c:363
    diff --git a/doc/html/qdatabase_8c.js b/doc/html/qdatabase_8c.js deleted file mode 100644 index 1f84ecb6..00000000 --- a/doc/html/qdatabase_8c.js +++ /dev/null @@ -1,27 +0,0 @@ -var qdatabase_8c = -[ - [ "qdb", "qdatabase_8c.html#a4ba4b70bed91492b511152692005ace2", null ], - [ "open_", "qdatabase_8c.html#ae90a2206952d27df4a58995628741134", null ], - [ "close_", "qdatabase_8c.html#a89fc888110ad23e6358d155e2c9b6a3b", null ], - [ "execute_update", "qdatabase_8c.html#a42069ce53c9e45b7283e9e44a7e021d9", null ], - [ "execute_updatef", "qdatabase_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa", null ], - [ "execute_query", "qdatabase_8c.html#a1cb9916c08e1bef730d2f650f8c82a93", null ], - [ "execute_queryf", "qdatabase_8c.html#a375ef96e283f327b2c4b3e745c1214a3", null ], - [ "begin_tran", "qdatabase_8c.html#a2f27adbcda036187a4714e5bcd3a247e", null ], - [ "commit", "qdatabase_8c.html#a3386b5894d03e2f48deecb0b7c0632ea", null ], - [ "rollback", "qdatabase_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7", null ], - [ "set_fetchtype", "qdatabase_8c.html#a5dc988c35746fe8a792702523abc66b3", null ], - [ "get_conn_status", "qdatabase_8c.html#a0598e067ed6b749c9df042cd7bc83f5c", null ], - [ "ping", "qdatabase_8c.html#a998f1143e4b00ff847f120e34a0efc0d", null ], - [ "get_error", "qdatabase_8c.html#afeaa9b2ed0cde83bc01cb2996f8e5467", null ], - [ "free_", "qdatabase_8c.html#afe5578f18ea12e68e96a80b91bd9be9d", null ], - [ "_resultGetStr", "qdatabase_8c.html#a4b0bd360d7253d390c5775dd2608caa1", null ], - [ "_resultGetStrAt", "qdatabase_8c.html#a59dbd0f78d2490fd50b2254451103368", null ], - [ "_resultGetInt", "qdatabase_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1", null ], - [ "_resultGetIntAt", "qdatabase_8c.html#a6b5beda54ba78647276bc180f65138ca", null ], - [ "_resultGetNext", "qdatabase_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09", null ], - [ "result_get_cols", "qdatabase_8c.html#a95d88451db51492ae6122701ce56b94d", null ], - [ "result_get_rows", "qdatabase_8c.html#a26f8b55348c57f467d64c002ab306ff6", null ], - [ "result_get_row", "qdatabase_8c.html#a396a7328f7fb499f89c2652598185dc8", null ], - [ "result_free", "qdatabase_8c.html#a4ab66a07fa82b706895e1b2c19fcd501", null ] -]; \ No newline at end of file diff --git a/doc/html/qdatabase_8c_source.html b/doc/html/qdatabase_8c_source.html deleted file mode 100644 index d38613fc..00000000 --- a/doc/html/qdatabase_8c_source.html +++ /dev/null @@ -1,971 +0,0 @@ - - - - - - - -qLibc: extensions/qdatabase.c Source File - - - - - - - - - - -
    -
    - - - - - - -
    -
    qLibc -
    -
    -
    - - - - - - -
    -
    - -
    -
    -
    - -
    -
    -
    qdatabase.c
    -
    -
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qdatabase.c Database wrapper.
    -
    31 *
    -
    32 * Database header files should be included prior to qlibcext.h in your source
    -
    33 * codes like below.
    -
    34 *
    -
    35 * @code
    -
    36 * #include "mysql.h"
    -
    37 * #include "qlibcext.h"
    -
    38 * @endcode
    -
    39 *
    -
    40 * @code
    -
    41 * qdb_t *db = NULL;
    -
    42 * qdbresult_t *result = NULL;
    -
    43 *
    -
    44 * db = qdb("MYSQL", "dbhost.qdecoder.org", 3306,
    -
    45 * "test", "secret", "sampledb", true);
    -
    46 * if (db == NULL) {
    -
    47 * printf("ERROR: Not supported database type.\n");
    -
    48 * return -1;
    -
    49 * }
    -
    50 *
    -
    51 * // try to connect
    -
    52 * if (db->open(db) == false) {
    -
    53 * printf("WARNING: Can't connect to database.\n");
    -
    54 * return -1;
    -
    55 * }
    -
    56 *
    -
    57 * // get results
    -
    58 * result = db->execute_query(db, "SELECT name, population FROM City");
    -
    59 * if (result != NULL) {
    -
    60 * printf("COLS : %d , ROWS : %d\n",
    -
    61 * result->get_cols(result), result->get_rows(result));
    -
    62 * while (result->get_next(result) == true) {
    -
    63 * char *pszName = result->get_str(result, "name");
    -
    64 * int nPopulation = result->get_int(result, "population");
    -
    65 * printf("Country : %s , Population : %d\n", pszName, nPopulation);
    -
    66 * }
    -
    67 * result->free(result);
    -
    68 * }
    -
    69 *
    -
    70 * // close connection
    -
    71 * db->close(db);
    -
    72 *
    -
    73 * // free db object
    -
    74 * db->free(db);
    -
    75 * @endcode
    -
    76 */
    -
    77
    -
    78#ifndef DISABLE_QDATABASE
    -
    79
    -
    80#if defined(ENABLE_MYSQL) || defined( _DOXYGEN_SKIP)
    -
    81
    -
    82#ifdef ENABLE_MYSQL
    -
    83#include "mysql.h"
    -
    84/* mysql specific connector options */
    -
    85#define Q_MYSQL_OPT_RECONNECT (1)
    -
    86#define Q_MYSQL_OPT_CONNECT_TIMEOUT (10)
    -
    87#define Q_MYSQL_OPT_READ_TIMEOUT (30)
    -
    88#define Q_MYSQL_OPT_WRITE_TIMEOUT (30)
    -
    89#endif
    -
    90
    -
    91#include <stdio.h>
    -
    92#include <stdlib.h>
    -
    93#include <stdbool.h>
    -
    94#include <stdarg.h>
    -
    95#include <string.h>
    -
    96#include "qinternal.h"
    -
    97#include "extensions/qdatabase.h"
    -
    98
    -
    99/*
    -
    100 * Member method protos
    -
    101 */
    -
    102#ifndef _DOXYGEN_SKIP
    -
    103// qdb_t object
    -
    104static bool open_(qdb_t *db);
    -
    105static bool close_(qdb_t *db);
    -
    106
    -
    107static int execute_update(qdb_t *db, const char *query);
    -
    108static int execute_updatef(qdb_t *db, const char *format, ...);
    -
    109static qdbresult_t *execute_query(qdb_t *db, const char *query);
    -
    110static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...);
    -
    111
    -
    112static bool begin_tran(qdb_t *db);
    -
    113static bool commit(qdb_t *db);
    -
    114static bool rollback(qdb_t *db);
    -
    115
    -
    116static bool set_fetchtype(qdb_t *db, bool use);
    -
    117static bool get_conn_status(qdb_t *db);
    -
    118static bool ping(qdb_t *db);
    -
    119static const char *get_error(qdb_t *db, unsigned int *errorno);
    -
    120static void free_(qdb_t *db);
    -
    121
    -
    122// qdbresult_t object
    -
    123static const char *_resultGetStr(qdbresult_t *result, const char *field);
    -
    124static const char *_resultGetStrAt(qdbresult_t *result, int idx);
    -
    125static int _resultGetInt(qdbresult_t *result, const char *field);
    -
    126static int _resultGetIntAt(qdbresult_t *result, int idx);
    -
    127static bool _resultGetNext(qdbresult_t *result);
    -
    128
    -
    129static int result_get_cols(qdbresult_t *result);
    -
    130static int result_get_rows(qdbresult_t *result);
    -
    131static int result_get_row(qdbresult_t *result);
    -
    132
    -
    133static void result_free(qdbresult_t *result);
    -
    134
    -
    135#endif
    -
    136
    -
    137/**
    -
    138 * Initialize internal connector structure
    -
    139 *
    -
    140 * @param dbtype database server type. currently "MYSQL" is only supported
    -
    141 * @param addr ip or fqdn address.
    -
    142 * @param port port number
    -
    143 * @param username database username
    -
    144 * @param password database password
    -
    145 * @param database database server type. currently "MYSQL" is only supported
    -
    146 * @param autocommit sets autocommit mode on if autocommit is true, off if
    -
    147 * autocommit is false
    -
    148 *
    -
    149 * @return a pointer of qdb_t object in case of successful,
    -
    150 * otherwise returns NULL.
    -
    151 *
    -
    152 * @code
    -
    153 * qdb_t *db = qdb("MYSQL",
    -
    154 * "dbhost.qdecoder.org", 3306, "test", "secret",
    -
    155 * "sampledb", true);
    -
    156 * if (db == NULL) {
    -
    157 * printf("ERROR: Not supported database type.\n");
    -
    158 * return -1;
    -
    159 * }
    -
    160 * @endcode
    -
    161 */
    -
    162qdb_t *qdb(const char *dbtype, const char *addr, int port, const char *username,
    -
    163 const char *password, const char *database, bool autocommit)
    -
    164{
    -
    165 // check db type
    -
    166#ifdef Q_ENABLE_MYSQL
    -
    167 if (strcmp(dbtype, "MYSQL")) return NULL;
    -
    168#else
    -
    169 return NULL;
    -
    170#endif
    -
    171 if (dbtype == NULL
    -
    172 || addr == NULL
    -
    173 || username == NULL
    -
    174 || password == NULL
    -
    175 || database == NULL) {
    -
    176 return NULL;
    -
    177 }
    -
    178
    -
    179 // initialize
    -
    180 qdb_t *db;
    -
    181 if ((db = (qdb_t *)malloc(sizeof(qdb_t))) == NULL) return NULL;
    -
    182 memset((void *)db, 0, sizeof(qdb_t));
    -
    183 db->connected = false;
    -
    184
    -
    185 // set common structure
    -
    186 db->info.dbtype = strdup(dbtype);
    -
    187 db->info.addr = strdup(addr);
    -
    188 db->info.port = port;
    -
    189 db->info.username = strdup(username);
    -
    190 db->info.password = strdup(password);
    -
    191 db->info.database = strdup(database);
    -
    192 db->info.autocommit = autocommit;
    -
    193 db->info.fetchtype = false;// store mode
    -
    194
    -
    195 // set db handler
    -
    196#ifdef Q_ENABLE_MYSQL
    -
    197 db->mysql = NULL;
    -
    198#endif
    -
    199
    -
    200 // assign methods
    -
    201 db->open = open_;
    -
    202 db->close = close_;
    -
    203
    -
    204 db->execute_update = execute_update;
    -
    205 db->execute_updatef = execute_updatef;
    -
    206 db->execute_query = execute_query;
    -
    207 db->execute_queryf = execute_queryf;
    -
    208
    -
    209 db->begin_tran = begin_tran;
    -
    210 db->commit = commit;
    -
    211 db->rollback = rollback;
    -
    212
    -
    213 db->set_fetchtype = set_fetchtype;
    -
    214 db->get_conn_status = get_conn_status;
    -
    215 db->ping = ping;
    -
    216 db->get_error = get_error;
    -
    217 db->free = free_;
    -
    218
    -
    219 // initialize recrusive mutex
    -
    220 Q_MUTEX_NEW(db->qmutex, true);
    -
    221
    -
    222 return db;
    -
    223}
    -
    224
    -
    225/**
    -
    226 * qdb->open(): Connect to database server
    -
    227 *
    -
    228 * @param db a pointer of qdb_t object
    -
    229 *
    -
    230 * @return true if successful, otherwise returns false.
    -
    231 */
    -
    232static bool open_(qdb_t *db)
    -
    233{
    -
    234 if (db == NULL) return false;
    -
    235
    -
    236 // if connected, close first
    -
    237 if (db->connected == true) {
    -
    238 close_(db);
    -
    239 }
    -
    240
    -
    241#ifdef Q_ENABLE_MYSQL
    -
    242 Q_MUTEX_ENTER(db->qmutex);
    -
    243
    -
    244 // initialize handler
    -
    245 if (db->mysql != NULL) close_(db);
    -
    246
    -
    247 if (mysql_library_init(0, NULL, NULL) != 0) {
    -
    248 Q_MUTEX_LEAVE(db->qmutex);
    -
    249 return false;
    -
    250 }
    -
    251
    -
    252 if ((db->mysql = mysql_init(NULL)) == NULL) {
    -
    253 Q_MUTEX_LEAVE(db->qmutex);
    -
    254 return false;
    -
    255 }
    -
    256
    -
    257 // set options
    -
    258 my_bool reconnect = Q_MYSQL_OPT_RECONNECT;
    -
    259 unsigned int connect_timeout = Q_MYSQL_OPT_CONNECT_TIMEOUT;
    -
    260 unsigned int read_timeout = Q_MYSQL_OPT_READ_TIMEOUT;
    -
    261 unsigned int write_timeout = Q_MYSQL_OPT_WRITE_TIMEOUT;
    -
    262
    -
    263 if (reconnect != false) {
    -
    264 mysql_options(db->mysql,
    -
    265 MYSQL_OPT_RECONNECT,
    -
    266 (char *)&reconnect);
    -
    267 }
    -
    268 if (connect_timeout > 0) {
    -
    269 mysql_options(db->mysql,
    -
    270 MYSQL_OPT_CONNECT_TIMEOUT,
    -
    271 (char *)&connect_timeout);
    -
    272 }
    -
    273 if (read_timeout > 0) {
    -
    274 mysql_options(db->mysql,
    -
    275 MYSQL_OPT_READ_TIMEOUT,
    -
    276 (char *)&read_timeout);
    -
    277 }
    -
    278 if (write_timeout > 0) {
    -
    279 mysql_options(db->mysql,
    -
    280 MYSQL_OPT_WRITE_TIMEOUT,
    -
    281 (char *)&write_timeout);
    -
    282 }
    -
    283
    -
    284 // try to connect
    -
    285 if (mysql_real_connect(db->mysql,
    -
    286 db->info.addr,
    -
    287 db->info.username,
    -
    288 db->info.password,
    -
    289 db->info.database,
    -
    290 db->info.port, NULL, 0) == NULL) {
    -
    291 close_(db); // free mysql handler
    -
    292 Q_MUTEX_LEAVE(db->qmutex);
    -
    293 return false;
    -
    294 }
    -
    295
    -
    296 // set auto-commit
    -
    297 if (mysql_autocommit(db->mysql, db->info.autocommit) != 0) {
    -
    298 close_(db); // free mysql handler
    -
    299 Q_MUTEX_LEAVE(db->qmutex);
    -
    300 return false;
    -
    301 }
    -
    302
    -
    303 // set flag
    -
    304 db->connected = true;
    -
    305 Q_MUTEX_LEAVE(db->qmutex);
    -
    306 return true;
    -
    307#else
    -
    308 return false;
    -
    309#endif
    -
    310}
    -
    311
    -
    312/**
    -
    313 * qdb->close(): Disconnect from database server
    -
    314 *
    -
    315 * @param db a pointer of qdb_t object
    -
    316 *
    -
    317 * @return true if successful, otherwise returns false.
    -
    318 *
    -
    319 * @note
    -
    320 * Unless you call qdb->free(), qdb_t object will keep the database
    -
    321 * information. So you can re-connect to database using qdb->open().
    -
    322 */
    -
    323static bool close_(qdb_t *db)
    -
    324{
    -
    325 if (db == NULL) return false;
    -
    326
    -
    327#ifdef Q_ENABLE_MYSQL
    -
    328 Q_MUTEX_ENTER(db->qmutex);
    -
    329
    -
    330 if (db->mysql != NULL) {
    -
    331 mysql_close(db->mysql);
    -
    332 db->mysql = NULL;
    -
    333 mysql_library_end();
    -
    334 }
    -
    335 db->connected = false;
    -
    336
    -
    337 Q_MUTEX_LEAVE(db->qmutex);
    -
    338 return true;
    -
    339#else
    -
    340 return false;
    -
    341#endif
    -
    342}
    -
    343
    -
    344/**
    -
    345 * qdb->execute_update(): Executes the update DML
    -
    346 *
    -
    347 * @param db a pointer of qdb_t object
    -
    348 * @param query query string
    -
    349 *
    -
    350 * @return a number of affected rows
    -
    351 */
    -
    352static int execute_update(qdb_t *db, const char *query)
    -
    353{
    -
    354 if (db == NULL || db->connected == false) return -1;
    -
    355
    -
    356#ifdef Q_ENABLE_MYSQL
    -
    357 Q_MUTEX_ENTER(db->qmutex);
    -
    358
    -
    359 int affected = -1;
    -
    360
    -
    361 // query
    -
    362 DEBUG("%s", query);
    -
    363 if (mysql_query(db->mysql, query) == 0) {
    -
    364 /* get affected rows */
    -
    365 if ((affected = mysql_affected_rows(db->mysql)) < 0) affected = -1;
    -
    366 }
    -
    367
    -
    368 Q_MUTEX_LEAVE(db->qmutex);
    -
    369 return affected;
    -
    370#else
    -
    371 return -1;
    -
    372#endif
    -
    373}
    -
    374
    -
    375/**
    -
    376 * qdb->execute_updatef(): Executes the formatted update DML
    -
    377 *
    -
    378 * @param db a pointer of qdb_t object
    -
    379 * @param format query string format
    -
    380 *
    -
    381 * @return a number of affected rows, otherwise returns -1
    -
    382 */
    -
    383static int execute_updatef(qdb_t *db, const char *format, ...)
    -
    384{
    -
    385 char *query;
    -
    386 DYNAMIC_VSPRINTF(query, format);
    -
    387 if (query == NULL) return -1;
    -
    388
    -
    389 int affected = execute_update(db, query);
    -
    390 free(query);
    -
    391
    -
    392 return affected;
    -
    393}
    -
    394
    -
    395/**
    -
    396 * qdb->execute_query(): Executes the query
    -
    397 *
    -
    398 * @param db a pointer of qdb_t object
    -
    399 * @param query query string
    -
    400 *
    -
    401 * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    -
    402 */
    -
    403static qdbresult_t *execute_query(qdb_t *db, const char *query)
    -
    404{
    -
    405 if (db == NULL || db->connected == false) return NULL;
    -
    406
    -
    407#ifdef Q_ENABLE_MYSQL
    -
    408 // query
    -
    409 DEBUG("%s", query);
    -
    410 if (mysql_query(db->mysql, query)) return NULL;
    -
    411
    -
    412 // store
    -
    413 qdbresult_t *result = (qdbresult_t *)malloc(sizeof(qdbresult_t));
    -
    414 if (result == NULL) return NULL;
    -
    415
    -
    416 result->fetchtype = db->info.fetchtype;
    -
    417 if (result->fetchtype == false) {
    -
    418 result->rs = mysql_store_result(db->mysql);
    -
    419 } else {
    -
    420 result->rs = mysql_use_result(db->mysql);
    -
    421 }
    -
    422 if (result->rs == NULL) {
    -
    423 free(result);
    -
    424 return NULL;
    -
    425 }
    -
    426
    -
    427 /* get meta data */
    -
    428 result->fields = NULL;
    -
    429 result->row = NULL;
    -
    430 result->cols = mysql_num_fields(result->rs);
    -
    431 result->cursor = 0;
    -
    432
    -
    433 /* assign methods */
    -
    434 result->get_str = _resultGetStr;
    -
    435 result->get_str_at = _resultGetStrAt;
    -
    436 result->get_int = _resultGetInt;
    -
    437 result->get_int_at = _resultGetIntAt;
    -
    438 result->get_next = _resultGetNext;
    -
    439
    -
    440 result->get_cols = result_get_cols;
    -
    441 result->get_rows = result_get_rows;
    -
    442 result->get_row = result_get_row;
    -
    443
    -
    444 result->free = result_free;
    -
    445
    -
    446 return result;
    -
    447#else
    -
    448 return NULL;
    -
    449#endif
    -
    450}
    -
    451
    -
    452/**
    -
    453 * qdb->execute_queryf(): Executes the formatted query
    -
    454 *
    -
    455 * @param db a pointer of qdb_t object
    -
    456 * @param format query string format
    -
    457 *
    -
    458 * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    -
    459 */
    -
    460static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...)
    -
    461{
    -
    462 char *query;
    -
    463 DYNAMIC_VSPRINTF(query, format);
    -
    464 if (query == NULL) return NULL;
    -
    465
    -
    466 qdbresult_t *ret = db->execute_query(db, query);
    -
    467 free(query);
    -
    468 return ret;
    -
    469}
    -
    470
    -
    471/**
    -
    472 * qdb->begin_tran(): Start transaction
    -
    473 *
    -
    474 * @param db a pointer of qdb_t object
    -
    475 *
    -
    476 * @return true if successful, otherwise returns false
    -
    477 *
    -
    478 * @code
    -
    479 * db->begin_tran(db);
    -
    480 * (... insert/update/delete ...)
    -
    481 * db->commit(db);
    -
    482 * @endcode
    -
    483 */
    -
    484static bool begin_tran(qdb_t *db)
    -
    485{
    -
    486 if (db == NULL) return false;
    -
    487
    -
    488#ifdef Q_ENABLE_MYSQL
    -
    489 Q_MUTEX_ENTER(db->qmutex);
    -
    490 if (db->qmutex.count != 1) {
    -
    491 Q_MUTEX_LEAVE(db->qmutex);
    -
    492 return false;
    -
    493 }
    -
    494
    -
    495 qdbresult_t *result;
    -
    496 result = db->execute_query(db, "START TRANSACTION");
    -
    497 if (result == NULL) {
    -
    498 Q_MUTEX_LEAVE(db->qmutex);
    -
    499 return false;
    -
    500 }
    -
    501 result->free(result);
    -
    502 return true;
    -
    503#else
    -
    504 return false;
    -
    505#endif
    -
    506}
    -
    507
    -
    508/**
    -
    509 * qdb->commit(): Commit transaction
    -
    510 *
    -
    511 * @param db a pointer of qdb_t object
    -
    512 *
    -
    513 * @return true if successful, otherwise returns false
    -
    514 */
    -
    515static bool commit(qdb_t *db)
    -
    516{
    -
    517 if (db == NULL) return false;
    -
    518
    -
    519#ifdef Q_ENABLE_MYSQL
    -
    520 bool ret = false;
    -
    521 if (mysql_commit(db->mysql) == 0) {
    -
    522 ret = true;
    -
    523 }
    -
    524
    -
    525 if (db->qmutex.count > 0) {
    -
    526 Q_MUTEX_LEAVE(db->qmutex);
    -
    527 }
    -
    528 return ret;
    -
    529#else
    -
    530 return false;
    -
    531#endif
    -
    532}
    -
    533
    -
    534/**
    -
    535 * qdb->rellback(): Roll-back and abort transaction
    -
    536 *
    -
    537 * @param db a pointer of qdb_t object
    -
    538 *
    -
    539 * @return true if successful, otherwise returns false
    -
    540 */
    -
    541static bool rollback(qdb_t *db)
    -
    542{
    -
    543 if (db == NULL) return false;
    -
    544
    -
    545#ifdef Q_ENABLE_MYSQL
    -
    546 bool ret = false;
    -
    547 if (mysql_rollback(db->mysql) == 0) {
    -
    548 ret = true;
    -
    549 }
    -
    550
    -
    551 if (db->qmutex.count > 0) {
    -
    552 Q_MUTEX_LEAVE(db->qmutex);
    -
    553 }
    -
    554 return ret;
    -
    555#else
    -
    556 return 0;
    -
    557#endif
    -
    558}
    -
    559
    -
    560/**
    -
    561 * qdb->set_fetchtype(): Set result fetching type
    -
    562 *
    -
    563 * @param db a pointer of qdb_t object
    -
    564 * @param fromdb false for storing the results to client (default mode),
    -
    565 * true for fetching directly from server,
    -
    566 *
    -
    567 * @return true if successful otherwise returns false
    -
    568 *
    -
    569 * @note
    -
    570 * If qdb->set_fetchtype(db, true) is called, the results does not
    -
    571 * actually read into the client. Instead, each row must be retrieved
    -
    572 * individually by making calls to qdbresult->get_next().
    -
    573 * This reads the result of a query directly from the server without storing
    -
    574 * it in local buffer, which is somewhat faster and uses much less memory than
    -
    575 * default behavior qdb->set_fetchtype(db, false).
    -
    576 */
    -
    577static bool set_fetchtype(qdb_t *db, bool fromdb)
    -
    578{
    -
    579 if (db == NULL) return false;
    -
    580 db->info.fetchtype = fromdb;
    -
    581 return true;
    -
    582}
    -
    583
    -
    584/**
    -
    585 * qdb->get_conn_status(): Get last connection status
    -
    586 *
    -
    587 * @param db a pointer of qdb_t object
    -
    588 *
    -
    589 * @return true if the connection flag is set to alive, otherwise returns false
    -
    590 *
    -
    591 * @note
    -
    592 * This function just returns the the connection status flag.
    -
    593 */
    -
    594static bool get_conn_status(qdb_t *db)
    -
    595{
    -
    596 if (db == NULL) return false;
    -
    597
    -
    598 return db->connected;
    -
    599}
    -
    600
    -
    601/**
    -
    602 * qdb->ping(): Checks whether the connection to the server is working.
    -
    603 *
    -
    604 * @param db a pointer of qdb_t object
    -
    605 *
    -
    606 * @return true if connection is alive, false if connection is not available
    -
    607 * and failed to reconnect
    -
    608 *
    -
    609 * @note
    -
    610 * If the connection has gone down, an attempt to reconnect.
    -
    611 */
    -
    612static bool ping(qdb_t *db)
    -
    613{
    -
    614 if (db == NULL) return false;
    -
    615
    -
    616#ifdef Q_ENABLE_MYSQL
    -
    617 if (db->connected == true && mysql_ping(db->mysql) == 0) {
    -
    618 return true;
    -
    619 } else { // ping test failed
    -
    620 if (open_(db) == true) { // try re-connect
    -
    621 DEBUG("Connection recovered.");
    -
    622 return true;
    -
    623 }
    -
    624 }
    -
    625
    -
    626 return false;
    -
    627#else
    -
    628 return false;
    -
    629#endif
    -
    630}
    -
    631
    -
    632/**
    -
    633 * qdb->get_error(): Get error number and message
    -
    634 *
    -
    635 * @param db a pointer of qdb_t object
    -
    636 * @param errorno if not NULL, error number will be stored
    -
    637 *
    -
    638 * @return a pointer of error message string
    -
    639 *
    -
    640 * @note
    -
    641 * Do not free returned error message
    -
    642 */
    -
    643static const char *get_error(qdb_t *db, unsigned int *errorno)
    -
    644{
    -
    645 if (db == NULL || db->connected == false) return "(no opened db)";
    -
    646
    -
    647 unsigned int eno = 0;
    -
    648 const char *emsg;
    -
    649#ifdef Q_ENABLE_MYSQL
    -
    650 eno = mysql_errno(db->mysql);
    -
    651 if (eno == 0) emsg = "(no error)";
    -
    652 else emsg = mysql_error(db->mysql);
    -
    653#else
    -
    654 emsg = "(not implemented)";
    -
    655#endif
    -
    656
    -
    657 if (errorno != NULL) *errorno = eno;
    -
    658 return emsg;
    -
    659}
    -
    660
    -
    661/**
    -
    662 * qdb->free(): De-allocate qdb_t structure
    -
    663 *
    -
    664 * @param db a pointer of qdb_t object
    -
    665 */
    -
    666static void free_(qdb_t *db)
    -
    667{
    -
    668 if (db == NULL) return;
    -
    669
    -
    670 Q_MUTEX_ENTER(db->qmutex);
    -
    671
    -
    672 close_(db);
    -
    673
    -
    674 free(db->info.dbtype);
    -
    675 free(db->info.addr);
    -
    676 free(db->info.username);
    -
    677 free(db->info.password);
    -
    678 free(db->info.database);
    -
    679 free(db);
    -
    680
    -
    681 Q_MUTEX_LEAVE(db->qmutex);
    -
    682 Q_MUTEX_DESTROY(db->qmutex);
    -
    683
    -
    684 return;
    -
    685}
    -
    686
    -
    687/**
    -
    688 * qdbresult->get_str(): Get the result as string by field name
    -
    689 *
    -
    690 * @param result a pointer of qdbresult_t
    -
    691 * @param field column name
    -
    692 *
    -
    693 * @return a string pointer if successful, otherwise returns NULL.
    -
    694 *
    -
    695 * @note
    -
    696 * Do not free returned string.
    -
    697 */
    -
    698static const char *_resultGetStr(qdbresult_t *result, const char *field)
    -
    699{
    -
    700#ifdef Q_ENABLE_MYSQL
    -
    701 if (result == NULL || result->rs == NULL || result->cols <= 0) return NULL;
    -
    702
    -
    703 if (result->fields == NULL) result->fields = mysql_fetch_fields(result->rs);
    -
    704
    -
    705 int i;
    -
    706 for (i = 0; i < result->cols; i++) {
    -
    707 if (!strcasecmp(result->fields[i].name, field)) {
    -
    708 return result->get_str_at(result, i + 1);
    -
    709 }
    -
    710 }
    -
    711
    -
    712 return NULL;
    -
    713#else
    -
    714 return NULL;
    -
    715#endif
    -
    716}
    -
    717
    -
    718/**
    -
    719 * qdbresult->get_str_at(): Get the result as string by column number
    -
    720 *
    -
    721 * @param result a pointer of qdbresult_t
    -
    722 * @param idx column number (first column is 1)
    -
    723 *
    -
    724 * @return a string pointer if successful, otherwise returns NULL.
    -
    725 */
    -
    726static const char *_resultGetStrAt(qdbresult_t *result, int idx)
    -
    727{
    -
    728#ifdef Q_ENABLE_MYSQL
    -
    729 if (result == NULL
    -
    730 || result->rs == NULL
    -
    731 || result->cursor == 0
    -
    732 || idx <= 0
    -
    733 || idx > result->cols ) {
    -
    734 return NULL;
    -
    735 }
    -
    736 return result->row[idx-1];
    -
    737#else
    -
    738 return NULL;
    -
    739#endif
    -
    740}
    -
    741
    -
    742/**
    -
    743 * qdbresult->get_int(): Get the result as integer by field name
    -
    744 *
    -
    745 * @param result a pointer of qdbresult_t
    -
    746 * @param field column name
    -
    747 *
    -
    748 * @return a integer converted value
    -
    749 */
    -
    750static int _resultGetInt(qdbresult_t *result, const char *field)
    -
    751{
    -
    752 const char *val = result->get_str(result, field);
    -
    753 if (val == NULL) return 0;
    -
    754 return atoi(val);
    -
    755}
    -
    756
    -
    757/**
    -
    758 * qdbresult->get_int_at(): Get the result as integer by column number
    -
    759 *
    -
    760 * @param result a pointer of qdbresult_t
    -
    761 * @param idx column number (first column is 1)
    -
    762 *
    -
    763 * @return a integer converted value
    -
    764 */
    -
    765static int _resultGetIntAt(qdbresult_t *result, int idx)
    -
    766{
    -
    767 const char *val = result->get_str_at(result, idx);
    -
    768 if (val == NULL) return 0;
    -
    769 return atoi(val);
    -
    770}
    -
    771
    -
    772/**
    -
    773 * qdbresult->get_next(): Retrieves the next row of a result set
    -
    774 *
    -
    775 * @param result a pointer of qdbresult_t
    -
    776 *
    -
    777 * @return true if successful, false if no more rows are left
    -
    778 */
    -
    779static bool _resultGetNext(qdbresult_t *result)
    -
    780{
    -
    781#ifdef Q_ENABLE_MYSQL
    -
    782 if (result == NULL || result->rs == NULL) return false;
    -
    783
    -
    784 if ((result->row = mysql_fetch_row(result->rs)) == NULL) return false;
    -
    785 result->cursor++;
    -
    786
    -
    787 return true;
    -
    788#else
    -
    789 return false;
    -
    790#endif
    -
    791}
    -
    792
    -
    793/**
    -
    794 * qdbresult->get_cols(): Get the number of columns in the result set
    -
    795 *
    -
    796 * @param result a pointer of qdbresult_t
    -
    797 *
    -
    798 * @return the number of columns in the result set
    -
    799 */
    -
    800static int result_get_cols(qdbresult_t *result)
    -
    801{
    -
    802#ifdef Q_ENABLE_MYSQL
    -
    803 if (result == NULL || result->rs == NULL) return 0;
    -
    804 return result->cols;
    -
    805#else
    -
    806 return 0;
    -
    807#endif
    -
    808}
    -
    809
    -
    810/**
    -
    811 * qdbresult->get_rows(): Get the number of rows in the result set
    -
    812 *
    -
    813 * @param result a pointer of qdbresult_t
    -
    814 *
    -
    815 * @return the number of rows in the result set
    -
    816 */
    -
    817static int result_get_rows(qdbresult_t *result)
    -
    818{
    -
    819#ifdef Q_ENABLE_MYSQL
    -
    820 if (result == NULL || result->rs == NULL) return 0;
    -
    821 return mysql_num_rows(result->rs);
    -
    822#else
    -
    823 return 0;
    -
    824#endif
    -
    825}
    -
    826
    -
    827/**
    -
    828 * qdbresult->get_row(): Get the current row number
    -
    829 *
    -
    830 * @param result a pointer of qdbresult_t
    -
    831 *
    -
    832 * @return current fetching row number of the result set
    -
    833 *
    -
    834 * @note
    -
    835 * This number is sequencial counter which is started from 1.
    -
    836 */
    -
    837static int result_get_row(qdbresult_t *result)
    -
    838{
    -
    839#ifdef Q_ENABLE_MYSQL
    -
    840 if (result == NULL || result->rs == NULL) return 0;
    -
    841 return result->cursor;
    -
    842#else
    -
    843 return 0;
    -
    844#endif
    -
    845}
    -
    846
    -
    847/**
    -
    848 * qdbresult->free(): De-allocate the result
    -
    849 *
    -
    850 * @param result a pointer of qdbresult_t
    -
    851 */
    -
    852static void result_free(qdbresult_t *result)
    -
    853{
    -
    854#ifdef Q_ENABLE_MYSQL
    -
    855 if (result == NULL) return;
    -
    856 if (result->rs != NULL) {
    -
    857 if (result->fetchtype == true) {
    -
    858 while (mysql_fetch_row(result->rs) != NULL);
    -
    859 }
    -
    860 mysql_free_result(result->rs);
    -
    861 result->rs = NULL;
    -
    862 }
    -
    863 free(result);
    -
    864 return;
    -
    865#else
    -
    866 return;
    -
    867#endif
    -
    868}
    -
    869
    -
    870#endif
    -
    871
    -
    872#endif /* DISABLE_QDATABASE */
    -
    static bool get_conn_status(qdb_t *db)
    qdb->get_conn_status(): Get last connection status
    Definition qdatabase.c:594
    -
    static qdbresult_t * execute_query(qdb_t *db, const char *query)
    qdb->execute_query(): Executes the query
    Definition qdatabase.c:403
    -
    static int result_get_rows(qdbresult_t *result)
    qdbresult->get_rows(): Get the number of rows in the result set
    Definition qdatabase.c:817
    -
    static bool begin_tran(qdb_t *db)
    qdb->begin_tran(): Start transaction
    Definition qdatabase.c:484
    -
    static bool commit(qdb_t *db)
    qdb->commit(): Commit transaction
    Definition qdatabase.c:515
    -
    static qdbresult_t * execute_queryf(qdb_t *db, const char *format,...)
    qdb->execute_queryf(): Executes the formatted query
    Definition qdatabase.c:460
    -
    static int result_get_row(qdbresult_t *result)
    qdbresult->get_row(): Get the current row number
    Definition qdatabase.c:837
    -
    static int execute_update(qdb_t *db, const char *query)
    qdb->execute_update(): Executes the update DML
    Definition qdatabase.c:352
    -
    static void result_free(qdbresult_t *result)
    qdbresult->free(): De-allocate the result
    Definition qdatabase.c:852
    -
    static const char * _resultGetStr(qdbresult_t *result, const char *field)
    qdbresult->get_str(): Get the result as string by field name
    Definition qdatabase.c:698
    -
    qdb_t * qdb(const char *dbtype, const char *addr, int port, const char *username, const char *password, const char *database, bool autocommit)
    Initialize internal connector structure.
    Definition qdatabase.c:162
    -
    static const char * _resultGetStrAt(qdbresult_t *result, int idx)
    qdbresult->get_str_at(): Get the result as string by column number
    Definition qdatabase.c:726
    -
    static bool set_fetchtype(qdb_t *db, bool fromdb)
    qdb->set_fetchtype(): Set result fetching type
    Definition qdatabase.c:577
    -
    static int _resultGetIntAt(qdbresult_t *result, int idx)
    qdbresult->get_int_at(): Get the result as integer by column number
    Definition qdatabase.c:765
    -
    static bool rollback(qdb_t *db)
    qdb->rellback(): Roll-back and abort transaction
    Definition qdatabase.c:541
    -
    static bool close_(qdb_t *db)
    qdb->close(): Disconnect from database server
    Definition qdatabase.c:323
    -
    static int result_get_cols(qdbresult_t *result)
    qdbresult->get_cols(): Get the number of columns in the result set
    Definition qdatabase.c:800
    -
    static bool ping(qdb_t *db)
    qdb->ping(): Checks whether the connection to the server is working.
    Definition qdatabase.c:612
    -
    static int execute_updatef(qdb_t *db, const char *format,...)
    qdb->execute_updatef(): Executes the formatted update DML
    Definition qdatabase.c:383
    -
    static int _resultGetInt(qdbresult_t *result, const char *field)
    qdbresult->get_int(): Get the result as integer by field name
    Definition qdatabase.c:750
    -
    static bool open_(qdb_t *db)
    qdb->open(): Connect to database server
    Definition qdatabase.c:232
    -
    static bool _resultGetNext(qdbresult_t *result)
    qdbresult->get_next(): Retrieves the next row of a result set
    Definition qdatabase.c:779
    -
    static void free_(qdb_t *db)
    qdb->free(): De-allocate qdb_t structure
    Definition qdatabase.c:666
    -
    static const char * get_error(qdb_t *db, unsigned int *errorno)
    qdb->get_error(): Get error number and message
    Definition qdatabase.c:643
    -
    -
    - - - - diff --git a/doc/html/qdatabase_8c.html b/doc/html/qdatabase__mysql_8c.html similarity index 70% rename from doc/html/qdatabase_8c.html rename to doc/html/qdatabase__mysql_8c.html index 8e0823a0..8b2bcff4 100644 --- a/doc/html/qdatabase_8c.html +++ b/doc/html/qdatabase__mysql_8c.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: extensions/qdatabase.c File Reference +qLibc: extensions/qdatabase_mysql.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,96 +52,97 @@

    -
    qdatabase.c File Reference
    +
    +
    qdatabase_mysql.c File Reference

    Database wrapper. More...

    -

    Go to the source code of this file.

    +

    Go to the source code of this file.

    - - - - - - + + + + + - - + + - - + + - - + + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + +

    +

    Functions

    qdb_t * qdb (const char *dbtype, const char *addr, int port, const char *username, const char *password, const char *database, bool autocommit)
     Initialize internal connector structure.
     
    static bool open_ (qdb_t *db)
     qdb->open(): Connect to database server
    qdb_t * qdb_mysql (const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
     Initialize internal connector structure. More...
     
    static bool open_ (qdb_t *db)
     qdb->open(): Connect to database server More...
     
    static bool close_ (qdb_t *db)
     qdb->close(): Disconnect from database server
    static bool close_ (qdb_t *db)
     qdb->close(): Disconnect from database server More...
     
    static int execute_update (qdb_t *db, const char *query)
     qdb->execute_update(): Executes the update DML
    static int execute_update (qdb_t *db, const char *query)
     qdb->execute_update(): Executes the update DML More...
     
    static int execute_updatef (qdb_t *db, const char *format,...)
     qdb->execute_updatef(): Executes the formatted update DML
    static int execute_updatef (qdb_t *db, const char *format,...)
     qdb->execute_updatef(): Executes the formatted update DML More...
     
    static qdbresult_t * execute_query (qdb_t *db, const char *query)
     qdb->execute_query(): Executes the query
     
    static qdbresult_t * execute_queryf (qdb_t *db, const char *format,...)
     qdb->execute_queryf(): Executes the formatted query
     
    static bool begin_tran (qdb_t *db)
     qdb->begin_tran(): Start transaction
    static qdbresult_t * execute_query (qdb_t *db, const char *query)
     qdb->execute_query(): Executes the query More...
     
    static qdbresult_t * execute_queryf (qdb_t *db, const char *format,...)
     qdb->execute_queryf(): Executes the formatted query More...
     
    static bool begin_tran (qdb_t *db)
     qdb->begin_tran(): Start transaction More...
     
    static bool commit (qdb_t *db)
     qdb->commit(): Commit transaction
    static bool commit (qdb_t *db)
     qdb->commit(): Commit transaction More...
     
    static bool rollback (qdb_t *db)
     qdb->rellback(): Roll-back and abort transaction
    static bool rollback (qdb_t *db)
     qdb->rellback(): Roll-back and abort transaction More...
     
    static bool set_fetchtype (qdb_t *db, bool fromdb)
     qdb->set_fetchtype(): Set result fetching type
    static bool set_fetchtype (qdb_t *db, bool fromdb)
     qdb->set_fetchtype(): Set result fetching type More...
     
    static bool get_conn_status (qdb_t *db)
     qdb->get_conn_status(): Get last connection status
    static bool get_conn_status (qdb_t *db)
     qdb->get_conn_status(): Get last connection status More...
     
    static bool ping (qdb_t *db)
     qdb->ping(): Checks whether the connection to the server is working.
    static bool ping (qdb_t *db)
     qdb->ping(): Checks whether the connection to the server is working. More...
     
    static const char * get_error (qdb_t *db, unsigned int *errorno)
     qdb->get_error(): Get error number and message
     
    static void free_ (qdb_t *db)
     qdb->free(): De-allocate qdb_t structure
    static const char * get_error (qdb_t *db, unsigned int *errorno)
     qdb->get_error(): Get error number and message More...
     
    static void free_ (qdb_t *db)
     qdb->free(): De-allocate qdb_t structure More...
     
    static const char * _resultGetStr (qdbresult_t *result, const char *field)
     qdbresult->get_str(): Get the result as string by field name
     
    static const char * _resultGetStrAt (qdbresult_t *result, int idx)
     qdbresult->get_str_at(): Get the result as string by column number
     
    static int _resultGetInt (qdbresult_t *result, const char *field)
     qdbresult->get_int(): Get the result as integer by field name
    static const char * _resultGetStr (qdbresult_t *result, const char *field)
     qdbresult->get_str(): Get the result as string by field name More...
     
    static const char * _resultGetStrAt (qdbresult_t *result, int idx)
     qdbresult->get_str_at(): Get the result as string by column number More...
     
    static int _resultGetInt (qdbresult_t *result, const char *field)
     qdbresult->get_int(): Get the result as integer by field name More...
     
    static int _resultGetIntAt (qdbresult_t *result, int idx)
     qdbresult->get_int_at(): Get the result as integer by column number
    static int _resultGetIntAt (qdbresult_t *result, int idx)
     qdbresult->get_int_at(): Get the result as integer by column number More...
     
    static bool _resultGetNext (qdbresult_t *result)
     qdbresult->get_next(): Retrieves the next row of a result set
    static bool _resultGetNext (qdbresult_t *result)
     qdbresult->get_next(): Retrieves the next row of a result set More...
     
    static int result_get_cols (qdbresult_t *result)
     qdbresult->get_cols(): Get the number of columns in the result set
    static int result_get_cols (qdbresult_t *result)
     qdbresult->get_cols(): Get the number of columns in the result set More...
     
    static int result_get_rows (qdbresult_t *result)
     qdbresult->get_rows(): Get the number of rows in the result set
    static int result_get_rows (qdbresult_t *result)
     qdbresult->get_rows(): Get the number of rows in the result set More...
     
    static int result_get_row (qdbresult_t *result)
     qdbresult->get_row(): Get the current row number
    static int result_get_row (qdbresult_t *result)
     qdbresult->get_row(): Get the current row number More...
     
    static void result_free (qdbresult_t *result)
     qdbresult->free(): De-allocate the result
    static void result_free (qdbresult_t *result)
     qdbresult->free(): De-allocate the result More...
     

    Detailed Description

    @@ -154,7 +154,7 @@
    qdb_t *db = NULL;
    qdbresult_t *result = NULL;
    -
    db = qdb("MYSQL", "dbhost.qdecoder.org", 3306,
    +
    db = qdb("MYSQL", "dbhost.qdecoder.org", 3306,
    "test", "secret", "sampledb", true);
    if (db == NULL) {
    printf("ERROR: Not supported database type.\n");
    @@ -185,18 +185,17 @@
    // free db object
    db->free(db);
    -
    qdb_t * qdb(const char *dbtype, const char *addr, int port, const char *username, const char *password, const char *database, bool autocommit)
    Initialize internal connector structure.
    Definition qdatabase.c:162
    -

    Definition in file qdatabase.c.

    +

    Definition in file qdatabase_mysql.c.

    Function Documentation

    - -

    ◆ qdb()

    + +

    ◆ qdb_mysql()

    - + @@ -217,19 +216,19 @@

    - + - + - + @@ -259,20 +258,21 @@

    Returns
    a pointer of qdb_t object in case of successful, otherwise returns NULL.
    -
    +
    qdb_t *db = qdb_mysql("MYSQL",
    "dbhost.qdecoder.org", 3306, "test", "secret",
    "sampledb", true);
    if (db == NULL) {
    printf("ERROR: Not supported database type.\n");
    return -1;
    }
    +
    qdb_t * qdb_mysql(const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
    Initialize internal connector structure.
    -

    Definition at line 162 of file qdatabase.c.

    +

    Definition at line 162 of file qdatabase_mysql.c.

    - -

    ◆ open_()

    + +

    ◆ open_()

    - -

    ◆ close_()

    + +

    ◆ close_()

    - -

    ◆ execute_update()

    + +

    ◆ execute_update()

    @@ -380,7 +380,7 @@

    -

    qdb->execute_update(): Executes the update DML

    +

    qdb->execute_update(): Executes the update DML

    Parameters

    qdb_t * qdb qdb_t* qdb_mysql ( const char *  dbtype, const char * username, database,
    const char * password, username,
    const char * database, password,
    @@ -390,12 +390,12 @@

    Returns
    a number of affected rows
    -

    Definition at line 352 of file qdatabase.c.

    +

    Definition at line 352 of file qdatabase_mysql.c.

    - -

    ◆ execute_updatef()

    + +

    ◆ execute_updatef()

    @@ -434,7 +434,7 @@

    -

    qdb->execute_updatef(): Executes the formatted update DML

    +

    qdb->execute_updatef(): Executes the formatted update DML

    Parameters

    dba pointer of qdb_t object
    @@ -444,12 +444,12 @@

    Returns
    a number of affected rows, otherwise returns -1
    -

    Definition at line 383 of file qdatabase.c.

    +

    Definition at line 383 of file qdatabase_mysql.c.

    - -

    ◆ execute_query()

    + +

    ◆ execute_query()

    @@ -458,7 +458,7 @@

    dba pointer of qdb_t object
    - + @@ -482,7 +482,7 @@

    -

    qdb->execute_query(): Executes the query

    +

    qdb->execute_query(): Executes the query

    Parameters

    static qdbresult_t * execute_query static qdbresult_t* execute_query ( qdb_t *  db,
    @@ -492,12 +492,12 @@

    Returns
    a pointer of qdbresult_t if successful, otherwise returns NULL
    -

    Definition at line 403 of file qdatabase.c.

    +

    Definition at line 403 of file qdatabase_mysql.c.

    - -

    ◆ execute_queryf()

    + +

    ◆ execute_queryf()

    @@ -506,7 +506,7 @@

    dba pointer of qdb_t object
    - + @@ -536,7 +536,7 @@

    -

    qdb->execute_queryf(): Executes the formatted query

    +

    qdb->execute_queryf(): Executes the formatted query

    Parameters

    static qdbresult_t * execute_queryf static qdbresult_t* execute_queryf ( qdb_t *  db,
    @@ -546,12 +546,12 @@

    Returns
    a pointer of qdbresult_t if successful, otherwise returns NULL
    -

    Definition at line 460 of file qdatabase.c.

    +

    Definition at line 460 of file qdatabase_mysql.c.

    - -

    ◆ begin_tran()

    + +

    ◆ begin_tran()

    @@ -574,7 +574,7 @@

    -

    qdb->begin_tran(): Start transaction

    +

    qdb->begin_tran(): Start transaction

    Parameters

    dba pointer of qdb_t object
    @@ -586,12 +586,12 @@

    (... insert/update/delete ...)
    db->commit(db);
    -

    Definition at line 484 of file qdatabase.c.

    +

    Definition at line 484 of file qdatabase_mysql.c.

    - -

    ◆ commit()

    + +

    ◆ commit()

    @@ -614,7 +614,7 @@

    -

    qdb->commit(): Commit transaction

    +

    qdb->commit(): Commit transaction

    Parameters

    dba pointer of qdb_t object
    @@ -623,12 +623,12 @@

    Returns
    true if successful, otherwise returns false
    -

    Definition at line 515 of file qdatabase.c.

    +

    Definition at line 515 of file qdatabase_mysql.c.

    - -

    ◆ rollback()

    + +

    ◆ rollback()

    - -

    ◆ set_fetchtype()

    + +

    ◆ set_fetchtype()

    @@ -698,7 +698,7 @@

    -

    qdb->set_fetchtype(): Set result fetching type

    +

    qdb->set_fetchtype(): Set result fetching type

    Parameters

    dba pointer of qdb_t object
    @@ -709,12 +709,12 @@

    Returns
    true if successful otherwise returns false
    Note
    If qdb->set_fetchtype(db, true) is called, the results does not actually read into the client. Instead, each row must be retrieved individually by making calls to qdbresult->get_next(). This reads the result of a query directly from the server without storing it in local buffer, which is somewhat faster and uses much less memory than default behavior qdb->set_fetchtype(db, false).
    -

    Definition at line 577 of file qdatabase.c.

    +

    Definition at line 577 of file qdatabase_mysql.c.

    - -

    ◆ get_conn_status()

    + +

    ◆ get_conn_status()

    @@ -737,7 +737,7 @@

    -

    qdb->get_conn_status(): Get last connection status

    +

    qdb->get_conn_status(): Get last connection status

    Parameters

    dba pointer of qdb_t object
    @@ -747,12 +747,12 @@

    Returns
    true if the connection flag is set to alive, otherwise returns false
    Note
    This function just returns the the connection status flag.
    -

    Definition at line 594 of file qdatabase.c.

    +

    Definition at line 594 of file qdatabase_mysql.c.

    - -

    ◆ ping()

    + +

    ◆ ping()

    @@ -775,7 +775,7 @@

    -

    qdb->ping(): Checks whether the connection to the server is working.

    +

    qdb->ping(): Checks whether the connection to the server is working.

    Parameters

    dba pointer of qdb_t object
    @@ -785,12 +785,12 @@

    Returns
    true if connection is alive, false if connection is not available and failed to reconnect
    Note
    If the connection has gone down, an attempt to reconnect.
    -

    Definition at line 612 of file qdatabase.c.

    +

    Definition at line 612 of file qdatabase_mysql.c.

    - -

    ◆ get_error()

    + +

    ◆ get_error()

    @@ -799,7 +799,7 @@

    dba pointer of qdb_t object
    - + @@ -823,7 +823,7 @@

    -

    qdb->get_error(): Get error number and message

    +

    qdb->get_error(): Get error number and message

    Parameters

    static const char * get_error static const char* get_error ( qdb_t *  db,
    @@ -834,12 +834,12 @@

    Returns
    a pointer of error message string
    Note
    Do not free returned error message
    -

    Definition at line 643 of file qdatabase.c.

    +

    Definition at line 643 of file qdatabase_mysql.c.

    - -

    ◆ free_()

    + +

    ◆ free_()

    @@ -870,12 +870,12 @@

    Definition at line 666 of file qdatabase.c.

    +

    Definition at line 666 of file qdatabase_mysql.c.

    - -

    ◆ _resultGetStr()

    + +

    ◆ _resultGetStr()

    @@ -884,7 +884,7 @@

    dba pointer of qdb_t object
    - + @@ -919,12 +919,12 @@

    Returns
    a string pointer if successful, otherwise returns NULL.
    Note
    Do not free returned string.
    -

    Definition at line 698 of file qdatabase.c.

    +

    Definition at line 698 of file qdatabase_mysql.c.

    - -

    ◆ _resultGetStrAt()

    + +

    ◆ _resultGetStrAt()

    @@ -933,7 +933,7 @@

    static const char * _resultGetStr static const char* _resultGetStr ( qdbresult_t *  result,
    - + @@ -967,12 +967,12 @@

    Returns
    a string pointer if successful, otherwise returns NULL.
    -

    Definition at line 726 of file qdatabase.c.

    +

    Definition at line 726 of file qdatabase_mysql.c.

    - -

    ◆ _resultGetInt()

    + +

    ◆ _resultGetInt()

    @@ -1015,12 +1015,12 @@

    Returns
    a integer converted value
    -

    Definition at line 750 of file qdatabase.c.

    +

    Definition at line 750 of file qdatabase_mysql.c.

    - -

    ◆ _resultGetIntAt()

    + +

    ◆ _resultGetIntAt()

    @@ -1063,12 +1063,12 @@

    Returns
    a integer converted value
    -

    Definition at line 765 of file qdatabase.c.

    +

    Definition at line 765 of file qdatabase_mysql.c.

    - -

    ◆ _resultGetNext()

    + +

    ◆ _resultGetNext()

    - -

    ◆ result_get_cols()

    + +

    ◆ result_get_cols()

    - -

    ◆ result_get_rows()

    + +

    ◆ result_get_rows()

    @@ -1174,12 +1174,12 @@

    Returns
    the number of rows in the result set
    -

    Definition at line 817 of file qdatabase.c.

    +

    Definition at line 817 of file qdatabase_mysql.c.

    - -

    ◆ result_get_row()

    + +

    ◆ result_get_row()

    - -

    ◆ result_free()

    + +

    ◆ result_free()

    @@ -1248,7 +1248,7 @@

    Definition at line 852 of file qdatabase.c.

    +

    Definition at line 852 of file qdatabase_mysql.c.

    @@ -1257,8 +1257,8 @@

    diff --git a/doc/html/qdatabase__mysql_8c.js b/doc/html/qdatabase__mysql_8c.js new file mode 100644 index 00000000..f97f4ac5 --- /dev/null +++ b/doc/html/qdatabase__mysql_8c.js @@ -0,0 +1,27 @@ +var qdatabase__mysql_8c = +[ + [ "qdb_mysql", "qdatabase__mysql_8c.html#a73ad0b6255fb4ac3c2c6870c5dd4e350", null ], + [ "open_", "qdatabase__mysql_8c.html#ae90a2206952d27df4a58995628741134", null ], + [ "close_", "qdatabase__mysql_8c.html#a89fc888110ad23e6358d155e2c9b6a3b", null ], + [ "execute_update", "qdatabase__mysql_8c.html#a42069ce53c9e45b7283e9e44a7e021d9", null ], + [ "execute_updatef", "qdatabase__mysql_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa", null ], + [ "execute_query", "qdatabase__mysql_8c.html#abe4e5330b68b3fc9ff94b086939dba82", null ], + [ "execute_queryf", "qdatabase__mysql_8c.html#a6fde99c74f91137ac554244ac60344be", null ], + [ "begin_tran", "qdatabase__mysql_8c.html#a2f27adbcda036187a4714e5bcd3a247e", null ], + [ "commit", "qdatabase__mysql_8c.html#a3386b5894d03e2f48deecb0b7c0632ea", null ], + [ "rollback", "qdatabase__mysql_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7", null ], + [ "set_fetchtype", "qdatabase__mysql_8c.html#a5dc988c35746fe8a792702523abc66b3", null ], + [ "get_conn_status", "qdatabase__mysql_8c.html#a0598e067ed6b749c9df042cd7bc83f5c", null ], + [ "ping", "qdatabase__mysql_8c.html#a998f1143e4b00ff847f120e34a0efc0d", null ], + [ "get_error", "qdatabase__mysql_8c.html#a11f5b1c5de493c27e1d396f7593408a0", null ], + [ "free_", "qdatabase__mysql_8c.html#afe5578f18ea12e68e96a80b91bd9be9d", null ], + [ "_resultGetStr", "qdatabase__mysql_8c.html#a3c32ab62afb92fb6a55cfaff7c92bf42", null ], + [ "_resultGetStrAt", "qdatabase__mysql_8c.html#ae3559441aaa0ae5f19965612dccca8d5", null ], + [ "_resultGetInt", "qdatabase__mysql_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1", null ], + [ "_resultGetIntAt", "qdatabase__mysql_8c.html#a6b5beda54ba78647276bc180f65138ca", null ], + [ "_resultGetNext", "qdatabase__mysql_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09", null ], + [ "result_get_cols", "qdatabase__mysql_8c.html#a95d88451db51492ae6122701ce56b94d", null ], + [ "result_get_rows", "qdatabase__mysql_8c.html#a26f8b55348c57f467d64c002ab306ff6", null ], + [ "result_get_row", "qdatabase__mysql_8c.html#a396a7328f7fb499f89c2652598185dc8", null ], + [ "result_free", "qdatabase__mysql_8c.html#a4ab66a07fa82b706895e1b2c19fcd501", null ] +]; \ No newline at end of file diff --git a/doc/html/qdatabase__mysql_8c_source.html b/doc/html/qdatabase__mysql_8c_source.html new file mode 100644 index 00000000..86f6ece8 --- /dev/null +++ b/doc/html/qdatabase__mysql_8c_source.html @@ -0,0 +1,971 @@ + + + + + + + +qLibc: extensions/qdatabase_mysql.c Source File + + + + + + + + + + +
    +
    +

    static const char * _resultGetStrAt static const char* _resultGetStrAt ( qdbresult_t *  result,
    + + + + + +
    +
    qLibc +
    +
    +
    + + + + + + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    qdatabase_mysql.c
    +
    +
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qdatabase_mysql.c Database wrapper.
    +
    31  *
    +
    32  * Database header files should be included prior to qlibcext.h in your source
    +
    33  * codes like below.
    +
    34  *
    +
    35  * @code
    +
    36  * #include "mysql.h"
    +
    37  * #include "qlibcext.h"
    +
    38  * @endcode
    +
    39  *
    +
    40  * @code
    +
    41  * qdb_t *db = NULL;
    +
    42  * qdbresult_t *result = NULL;
    +
    43  *
    +
    44  * db = qdb("MYSQL", "dbhost.qdecoder.org", 3306,
    +
    45  * "test", "secret", "sampledb", true);
    +
    46  * if (db == NULL) {
    +
    47  * printf("ERROR: Not supported database type.\n");
    +
    48  * return -1;
    +
    49  * }
    +
    50  *
    +
    51  * // try to connect
    +
    52  * if (db->open(db) == false) {
    +
    53  * printf("WARNING: Can't connect to database.\n");
    +
    54  * return -1;
    +
    55  * }
    +
    56  *
    +
    57  * // get results
    +
    58  * result = db->execute_query(db, "SELECT name, population FROM City");
    +
    59  * if (result != NULL) {
    +
    60  * printf("COLS : %d , ROWS : %d\n",
    +
    61  * result->get_cols(result), result->get_rows(result));
    +
    62  * while (result->get_next(result) == true) {
    +
    63  * char *pszName = result->get_str(result, "name");
    +
    64  * int nPopulation = result->get_int(result, "population");
    +
    65  * printf("Country : %s , Population : %d\n", pszName, nPopulation);
    +
    66  * }
    +
    67  * result->free(result);
    +
    68  * }
    +
    69  *
    +
    70  * // close connection
    +
    71  * db->close(db);
    +
    72  *
    +
    73  * // free db object
    +
    74  * db->free(db);
    +
    75  * @endcode
    +
    76  */
    +
    77 
    +
    78 #ifndef DISABLE_QDATABASE
    +
    79 
    +
    80 #if defined(ENABLE_MYSQL) || defined( _DOXYGEN_SKIP)
    +
    81 
    +
    82 #ifdef ENABLE_MYSQL
    +
    83 #include "mysql.h"
    +
    84 /* mysql specific connector options */
    +
    85 #define Q_MYSQL_OPT_RECONNECT (1)
    +
    86 #define Q_MYSQL_OPT_CONNECT_TIMEOUT (10)
    +
    87 #define Q_MYSQL_OPT_READ_TIMEOUT (30)
    +
    88 #define Q_MYSQL_OPT_WRITE_TIMEOUT (30)
    +
    89 #endif
    +
    90 
    +
    91 #include <stdio.h>
    +
    92 #include <stdlib.h>
    +
    93 #include <stdbool.h>
    +
    94 #include <stdarg.h>
    +
    95 #include <string.h>
    +
    96 #include "qinternal.h"
    +
    97 #include "extensions/qdatabase.h"
    +
    98 
    +
    99 /*
    +
    100  * Member method protos
    +
    101  */
    +
    102 #ifndef _DOXYGEN_SKIP
    +
    103 // qdb_t object
    +
    104 static bool open_(qdb_t *db);
    +
    105 static bool close_(qdb_t *db);
    +
    106 
    +
    107 static int execute_update(qdb_t *db, const char *query);
    +
    108 static int execute_updatef(qdb_t *db, const char *format, ...);
    +
    109 static qdbresult_t *execute_query(qdb_t *db, const char *query);
    +
    110 static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...);
    +
    111 
    +
    112 static bool begin_tran(qdb_t *db);
    +
    113 static bool commit(qdb_t *db);
    +
    114 static bool rollback(qdb_t *db);
    +
    115 
    +
    116 static bool set_fetchtype(qdb_t *db, bool use);
    +
    117 static bool get_conn_status(qdb_t *db);
    +
    118 static bool ping(qdb_t *db);
    +
    119 static const char *get_error(qdb_t *db, unsigned int *errorno);
    +
    120 static void free_(qdb_t *db);
    +
    121 
    +
    122 // qdbresult_t object
    +
    123 static const char *_resultGetStr(qdbresult_t *result, const char *field);
    +
    124 static const char *_resultGetStrAt(qdbresult_t *result, int idx);
    +
    125 static int _resultGetInt(qdbresult_t *result, const char *field);
    +
    126 static int _resultGetIntAt(qdbresult_t *result, int idx);
    +
    127 static bool _resultGetNext(qdbresult_t *result);
    +
    128 
    +
    129 static int result_get_cols(qdbresult_t *result);
    +
    130 static int result_get_rows(qdbresult_t *result);
    +
    131 static int result_get_row(qdbresult_t *result);
    +
    132 
    +
    133 static void result_free(qdbresult_t *result);
    +
    134 
    +
    135 #endif
    +
    136 
    +
    137 /**
    +
    138  * Initialize internal connector structure
    +
    139  *
    +
    140  * @param dbtype database server type. currently "MYSQL" is only supported
    +
    141  * @param addr ip or fqdn address.
    +
    142  * @param port port number
    +
    143  * @param username database username
    +
    144  * @param password database password
    +
    145  * @param database database server type. currently "MYSQL" is only supported
    +
    146  * @param autocommit sets autocommit mode on if autocommit is true, off if
    +
    147  * autocommit is false
    +
    148  *
    +
    149  * @return a pointer of qdb_t object in case of successful,
    +
    150  * otherwise returns NULL.
    +
    151  *
    +
    152  * @code
    +
    153  * qdb_t *db = qdb_mysql("MYSQL",
    +
    154  * "dbhost.qdecoder.org", 3306, "test", "secret",
    +
    155  * "sampledb", true);
    +
    156  * if (db == NULL) {
    +
    157  * printf("ERROR: Not supported database type.\n");
    +
    158  * return -1;
    +
    159  * }
    +
    160  * @endcode
    +
    161  */
    +
    162 qdb_t *qdb_mysql(const char *dbtype, const char *addr, int port, const char *database,
    +
    163  const char *username, const char *password, bool autocommit)
    +
    164 {
    +
    165  // check db type
    +
    166 #ifdef Q_ENABLE_MYSQL
    +
    167  if (strcmp(dbtype, "MYSQL")) return NULL;
    +
    168 #else
    +
    169  return NULL;
    +
    170 #endif
    +
    171  if (dbtype == NULL
    +
    172  || addr == NULL
    +
    173  || username == NULL
    +
    174  || password == NULL
    +
    175  || database == NULL) {
    +
    176  return NULL;
    +
    177  }
    +
    178 
    +
    179  // initialize
    +
    180  qdb_t *db;
    +
    181  if ((db = (qdb_t *)malloc(sizeof(qdb_t))) == NULL) return NULL;
    +
    182  memset((void *)db, 0, sizeof(qdb_t));
    +
    183  db->connected = false;
    +
    184 
    +
    185  // set common structure
    +
    186  db->info.dbtype = strdup(dbtype);
    +
    187  db->info.addr = strdup(addr);
    +
    188  db->info.port = port;
    +
    189  db->info.username = strdup(username);
    +
    190  db->info.password = strdup(password);
    +
    191  db->info.database = strdup(database);
    +
    192  db->info.autocommit = autocommit;
    +
    193  db->info.fetchtype = false;// store mode
    +
    194 
    +
    195  // set db handler
    +
    196 #ifdef Q_ENABLE_MYSQL
    +
    197  db->mysql = NULL;
    +
    198 #endif
    +
    199 
    +
    200  // assign methods
    +
    201  db->open = open_;
    +
    202  db->close = close_;
    +
    203 
    +
    204  db->execute_update = execute_update;
    +
    205  db->execute_updatef = execute_updatef;
    +
    206  db->execute_query = execute_query;
    +
    207  db->execute_queryf = execute_queryf;
    +
    208 
    +
    209  db->begin_tran = begin_tran;
    +
    210  db->commit = commit;
    +
    211  db->rollback = rollback;
    +
    212 
    +
    213  db->set_fetchtype = set_fetchtype;
    +
    214  db->get_conn_status = get_conn_status;
    +
    215  db->ping = ping;
    +
    216  db->get_error = get_error;
    +
    217  db->free = free_;
    +
    218 
    +
    219  // initialize recrusive mutex
    +
    220  Q_MUTEX_NEW(db->qmutex, true);
    +
    221 
    +
    222  return db;
    +
    223 }
    +
    224 
    +
    225 /**
    +
    226  * qdb->open(): Connect to database server
    +
    227  *
    +
    228  * @param db a pointer of qdb_t object
    +
    229  *
    +
    230  * @return true if successful, otherwise returns false.
    +
    231  */
    +
    232 static bool open_(qdb_t *db)
    +
    233 {
    +
    234  if (db == NULL) return false;
    +
    235 
    +
    236  // if connected, close first
    +
    237  if (db->connected == true) {
    +
    238  close_(db);
    +
    239  }
    +
    240 
    +
    241 #ifdef Q_ENABLE_MYSQL
    +
    242  Q_MUTEX_ENTER(db->qmutex);
    +
    243 
    +
    244  // initialize handler
    +
    245  if (db->mysql != NULL) close_(db);
    +
    246 
    +
    247  if (mysql_library_init(0, NULL, NULL) != 0) {
    +
    248  Q_MUTEX_LEAVE(db->qmutex);
    +
    249  return false;
    +
    250  }
    +
    251 
    +
    252  if ((db->mysql = mysql_init(NULL)) == NULL) {
    +
    253  Q_MUTEX_LEAVE(db->qmutex);
    +
    254  return false;
    +
    255  }
    +
    256 
    +
    257  // set options
    +
    258  my_bool reconnect = Q_MYSQL_OPT_RECONNECT;
    +
    259  unsigned int connect_timeout = Q_MYSQL_OPT_CONNECT_TIMEOUT;
    +
    260  unsigned int read_timeout = Q_MYSQL_OPT_READ_TIMEOUT;
    +
    261  unsigned int write_timeout = Q_MYSQL_OPT_WRITE_TIMEOUT;
    +
    262 
    +
    263  if (reconnect != false) {
    +
    264  mysql_options(db->mysql,
    +
    265  MYSQL_OPT_RECONNECT,
    +
    266  (char *)&reconnect);
    +
    267  }
    +
    268  if (connect_timeout > 0) {
    +
    269  mysql_options(db->mysql,
    +
    270  MYSQL_OPT_CONNECT_TIMEOUT,
    +
    271  (char *)&connect_timeout);
    +
    272  }
    +
    273  if (read_timeout > 0) {
    +
    274  mysql_options(db->mysql,
    +
    275  MYSQL_OPT_READ_TIMEOUT,
    +
    276  (char *)&read_timeout);
    +
    277  }
    +
    278  if (write_timeout > 0) {
    +
    279  mysql_options(db->mysql,
    +
    280  MYSQL_OPT_WRITE_TIMEOUT,
    +
    281  (char *)&write_timeout);
    +
    282  }
    +
    283 
    +
    284  // try to connect
    +
    285  if (mysql_real_connect(db->mysql,
    +
    286  db->info.addr,
    +
    287  db->info.username,
    +
    288  db->info.password,
    +
    289  db->info.database,
    +
    290  db->info.port, NULL, 0) == NULL) {
    +
    291  close_(db); // free mysql handler
    +
    292  Q_MUTEX_LEAVE(db->qmutex);
    +
    293  return false;
    +
    294  }
    +
    295 
    +
    296  // set auto-commit
    +
    297  if (mysql_autocommit(db->mysql, db->info.autocommit) != 0) {
    +
    298  close_(db); // free mysql handler
    +
    299  Q_MUTEX_LEAVE(db->qmutex);
    +
    300  return false;
    +
    301  }
    +
    302 
    +
    303  // set flag
    +
    304  db->connected = true;
    +
    305  Q_MUTEX_LEAVE(db->qmutex);
    +
    306  return true;
    +
    307 #else
    +
    308  return false;
    +
    309 #endif
    +
    310 }
    +
    311 
    +
    312 /**
    +
    313  * qdb->close(): Disconnect from database server
    +
    314  *
    +
    315  * @param db a pointer of qdb_t object
    +
    316  *
    +
    317  * @return true if successful, otherwise returns false.
    +
    318  *
    +
    319  * @note
    +
    320  * Unless you call qdb->free(), qdb_t object will keep the database
    +
    321  * information. So you can re-connect to database using qdb->open().
    +
    322  */
    +
    323 static bool close_(qdb_t *db)
    +
    324 {
    +
    325  if (db == NULL) return false;
    +
    326 
    +
    327 #ifdef Q_ENABLE_MYSQL
    +
    328  Q_MUTEX_ENTER(db->qmutex);
    +
    329 
    +
    330  if (db->mysql != NULL) {
    +
    331  mysql_close(db->mysql);
    +
    332  db->mysql = NULL;
    +
    333  mysql_library_end();
    +
    334  }
    +
    335  db->connected = false;
    +
    336 
    +
    337  Q_MUTEX_LEAVE(db->qmutex);
    +
    338  return true;
    +
    339 #else
    +
    340  return false;
    +
    341 #endif
    +
    342 }
    +
    343 
    +
    344 /**
    +
    345  * qdb->execute_update(): Executes the update DML
    +
    346  *
    +
    347  * @param db a pointer of qdb_t object
    +
    348  * @param query query string
    +
    349  *
    +
    350  * @return a number of affected rows
    +
    351  */
    +
    352 static int execute_update(qdb_t *db, const char *query)
    +
    353 {
    +
    354  if (db == NULL || db->connected == false) return -1;
    +
    355 
    +
    356 #ifdef Q_ENABLE_MYSQL
    +
    357  Q_MUTEX_ENTER(db->qmutex);
    +
    358 
    +
    359  int affected = -1;
    +
    360 
    +
    361  // query
    +
    362  DEBUG("%s", query);
    +
    363  if (mysql_query(db->mysql, query) == 0) {
    +
    364  /* get affected rows */
    +
    365  if ((affected = mysql_affected_rows(db->mysql)) < 0) affected = -1;
    +
    366  }
    +
    367 
    +
    368  Q_MUTEX_LEAVE(db->qmutex);
    +
    369  return affected;
    +
    370 #else
    +
    371  return -1;
    +
    372 #endif
    +
    373 }
    +
    374 
    +
    375 /**
    +
    376  * qdb->execute_updatef(): Executes the formatted update DML
    +
    377  *
    +
    378  * @param db a pointer of qdb_t object
    +
    379  * @param format query string format
    +
    380  *
    +
    381  * @return a number of affected rows, otherwise returns -1
    +
    382  */
    +
    383 static int execute_updatef(qdb_t *db, const char *format, ...)
    +
    384 {
    +
    385  char *query;
    +
    386  DYNAMIC_VSPRINTF(query, format);
    +
    387  if (query == NULL) return -1;
    +
    388 
    +
    389  int affected = execute_update(db, query);
    +
    390  free(query);
    +
    391 
    +
    392  return affected;
    +
    393 }
    +
    394 
    +
    395 /**
    +
    396  * qdb->execute_query(): Executes the query
    +
    397  *
    +
    398  * @param db a pointer of qdb_t object
    +
    399  * @param query query string
    +
    400  *
    +
    401  * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    +
    402  */
    +
    403 static qdbresult_t *execute_query(qdb_t *db, const char *query)
    +
    404 {
    +
    405  if (db == NULL || db->connected == false) return NULL;
    +
    406 
    +
    407 #ifdef Q_ENABLE_MYSQL
    +
    408  // query
    +
    409  DEBUG("%s", query);
    +
    410  if (mysql_query(db->mysql, query)) return NULL;
    +
    411 
    +
    412  // store
    +
    413  qdbresult_t *result = (qdbresult_t *)malloc(sizeof(qdbresult_t));
    +
    414  if (result == NULL) return NULL;
    +
    415 
    +
    416  result->fetchtype = db->info.fetchtype;
    +
    417  if (result->fetchtype == false) {
    +
    418  result->rs = mysql_store_result(db->mysql);
    +
    419  } else {
    +
    420  result->rs = mysql_use_result(db->mysql);
    +
    421  }
    +
    422  if (result->rs == NULL) {
    +
    423  free(result);
    +
    424  return NULL;
    +
    425  }
    +
    426 
    +
    427  /* get meta data */
    +
    428  result->fields = NULL;
    +
    429  result->row = NULL;
    +
    430  result->cols = mysql_num_fields(result->rs);
    +
    431  result->cursor = 0;
    +
    432 
    +
    433  /* assign methods */
    +
    434  result->get_str = _resultGetStr;
    +
    435  result->get_str_at = _resultGetStrAt;
    +
    436  result->get_int = _resultGetInt;
    +
    437  result->get_int_at = _resultGetIntAt;
    +
    438  result->get_next = _resultGetNext;
    +
    439 
    +
    440  result->get_cols = result_get_cols;
    +
    441  result->get_rows = result_get_rows;
    +
    442  result->get_row = result_get_row;
    +
    443 
    +
    444  result->free = result_free;
    +
    445 
    +
    446  return result;
    +
    447 #else
    +
    448  return NULL;
    +
    449 #endif
    +
    450 }
    +
    451 
    +
    452 /**
    +
    453  * qdb->execute_queryf(): Executes the formatted query
    +
    454  *
    +
    455  * @param db a pointer of qdb_t object
    +
    456  * @param format query string format
    +
    457  *
    +
    458  * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    +
    459  */
    +
    460 static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...)
    +
    461 {
    +
    462  char *query;
    +
    463  DYNAMIC_VSPRINTF(query, format);
    +
    464  if (query == NULL) return NULL;
    +
    465 
    +
    466  qdbresult_t *ret = db->execute_query(db, query);
    +
    467  free(query);
    +
    468  return ret;
    +
    469 }
    +
    470 
    +
    471 /**
    +
    472  * qdb->begin_tran(): Start transaction
    +
    473  *
    +
    474  * @param db a pointer of qdb_t object
    +
    475  *
    +
    476  * @return true if successful, otherwise returns false
    +
    477  *
    +
    478  * @code
    +
    479  * db->begin_tran(db);
    +
    480  * (... insert/update/delete ...)
    +
    481  * db->commit(db);
    +
    482  * @endcode
    +
    483  */
    +
    484 static bool begin_tran(qdb_t *db)
    +
    485 {
    +
    486  if (db == NULL) return false;
    +
    487 
    +
    488 #ifdef Q_ENABLE_MYSQL
    +
    489  Q_MUTEX_ENTER(db->qmutex);
    +
    490  if (db->qmutex.count != 1) {
    +
    491  Q_MUTEX_LEAVE(db->qmutex);
    +
    492  return false;
    +
    493  }
    +
    494 
    +
    495  qdbresult_t *result;
    +
    496  result = db->execute_query(db, "START TRANSACTION");
    +
    497  if (result == NULL) {
    +
    498  Q_MUTEX_LEAVE(db->qmutex);
    +
    499  return false;
    +
    500  }
    +
    501  result->free(result);
    +
    502  return true;
    +
    503 #else
    +
    504  return false;
    +
    505 #endif
    +
    506 }
    +
    507 
    +
    508 /**
    +
    509  * qdb->commit(): Commit transaction
    +
    510  *
    +
    511  * @param db a pointer of qdb_t object
    +
    512  *
    +
    513  * @return true if successful, otherwise returns false
    +
    514  */
    +
    515 static bool commit(qdb_t *db)
    +
    516 {
    +
    517  if (db == NULL) return false;
    +
    518 
    +
    519 #ifdef Q_ENABLE_MYSQL
    +
    520  bool ret = false;
    +
    521  if (mysql_commit(db->mysql) == 0) {
    +
    522  ret = true;
    +
    523  }
    +
    524 
    +
    525  if (db->qmutex.count > 0) {
    +
    526  Q_MUTEX_LEAVE(db->qmutex);
    +
    527  }
    +
    528  return ret;
    +
    529 #else
    +
    530  return false;
    +
    531 #endif
    +
    532 }
    +
    533 
    +
    534 /**
    +
    535  * qdb->rellback(): Roll-back and abort transaction
    +
    536  *
    +
    537  * @param db a pointer of qdb_t object
    +
    538  *
    +
    539  * @return true if successful, otherwise returns false
    +
    540  */
    +
    541 static bool rollback(qdb_t *db)
    +
    542 {
    +
    543  if (db == NULL) return false;
    +
    544 
    +
    545 #ifdef Q_ENABLE_MYSQL
    +
    546  bool ret = false;
    +
    547  if (mysql_rollback(db->mysql) == 0) {
    +
    548  ret = true;
    +
    549  }
    +
    550 
    +
    551  if (db->qmutex.count > 0) {
    +
    552  Q_MUTEX_LEAVE(db->qmutex);
    +
    553  }
    +
    554  return ret;
    +
    555 #else
    +
    556  return 0;
    +
    557 #endif
    +
    558 }
    +
    559 
    +
    560 /**
    +
    561  * qdb->set_fetchtype(): Set result fetching type
    +
    562  *
    +
    563  * @param db a pointer of qdb_t object
    +
    564  * @param fromdb false for storing the results to client (default mode),
    +
    565  * true for fetching directly from server,
    +
    566  *
    +
    567  * @return true if successful otherwise returns false
    +
    568  *
    +
    569  * @note
    +
    570  * If qdb->set_fetchtype(db, true) is called, the results does not
    +
    571  * actually read into the client. Instead, each row must be retrieved
    +
    572  * individually by making calls to qdbresult->get_next().
    +
    573  * This reads the result of a query directly from the server without storing
    +
    574  * it in local buffer, which is somewhat faster and uses much less memory than
    +
    575  * default behavior qdb->set_fetchtype(db, false).
    +
    576  */
    +
    577 static bool set_fetchtype(qdb_t *db, bool fromdb)
    +
    578 {
    +
    579  if (db == NULL) return false;
    +
    580  db->info.fetchtype = fromdb;
    +
    581  return true;
    +
    582 }
    +
    583 
    +
    584 /**
    +
    585  * qdb->get_conn_status(): Get last connection status
    +
    586  *
    +
    587  * @param db a pointer of qdb_t object
    +
    588  *
    +
    589  * @return true if the connection flag is set to alive, otherwise returns false
    +
    590  *
    +
    591  * @note
    +
    592  * This function just returns the the connection status flag.
    +
    593  */
    +
    594 static bool get_conn_status(qdb_t *db)
    +
    595 {
    +
    596  if (db == NULL) return false;
    +
    597 
    +
    598  return db->connected;
    +
    599 }
    +
    600 
    +
    601 /**
    +
    602  * qdb->ping(): Checks whether the connection to the server is working.
    +
    603  *
    +
    604  * @param db a pointer of qdb_t object
    +
    605  *
    +
    606  * @return true if connection is alive, false if connection is not available
    +
    607  * and failed to reconnect
    +
    608  *
    +
    609  * @note
    +
    610  * If the connection has gone down, an attempt to reconnect.
    +
    611  */
    +
    612 static bool ping(qdb_t *db)
    +
    613 {
    +
    614  if (db == NULL) return false;
    +
    615 
    +
    616 #ifdef Q_ENABLE_MYSQL
    +
    617  if (db->connected == true && mysql_ping(db->mysql) == 0) {
    +
    618  return true;
    +
    619  } else { // ping test failed
    +
    620  if (open_(db) == true) { // try re-connect
    +
    621  DEBUG("Connection recovered.");
    +
    622  return true;
    +
    623  }
    +
    624  }
    +
    625 
    +
    626  return false;
    +
    627 #else
    +
    628  return false;
    +
    629 #endif
    +
    630 }
    +
    631 
    +
    632 /**
    +
    633  * qdb->get_error(): Get error number and message
    +
    634  *
    +
    635  * @param db a pointer of qdb_t object
    +
    636  * @param errorno if not NULL, error number will be stored
    +
    637  *
    +
    638  * @return a pointer of error message string
    +
    639  *
    +
    640  * @note
    +
    641  * Do not free returned error message
    +
    642  */
    +
    643 static const char *get_error(qdb_t *db, unsigned int *errorno)
    +
    644 {
    +
    645  if (db == NULL || db->connected == false) return "(no opened db)";
    +
    646 
    +
    647  unsigned int eno = 0;
    +
    648  const char *emsg;
    +
    649 #ifdef Q_ENABLE_MYSQL
    +
    650  eno = mysql_errno(db->mysql);
    +
    651  if (eno == 0) emsg = "(no error)";
    +
    652  else emsg = mysql_error(db->mysql);
    +
    653 #else
    +
    654  emsg = "(not implemented)";
    +
    655 #endif
    +
    656 
    +
    657  if (errorno != NULL) *errorno = eno;
    +
    658  return emsg;
    +
    659 }
    +
    660 
    +
    661 /**
    +
    662  * qdb->free(): De-allocate qdb_t structure
    +
    663  *
    +
    664  * @param db a pointer of qdb_t object
    +
    665  */
    +
    666 static void free_(qdb_t *db)
    +
    667 {
    +
    668  if (db == NULL) return;
    +
    669 
    +
    670  Q_MUTEX_ENTER(db->qmutex);
    +
    671 
    +
    672  close_(db);
    +
    673 
    +
    674  free(db->info.dbtype);
    +
    675  free(db->info.addr);
    +
    676  free(db->info.username);
    +
    677  free(db->info.password);
    +
    678  free(db->info.database);
    +
    679  free(db);
    +
    680 
    +
    681  Q_MUTEX_LEAVE(db->qmutex);
    +
    682  Q_MUTEX_DESTROY(db->qmutex);
    +
    683 
    +
    684  return;
    +
    685 }
    +
    686 
    +
    687 /**
    +
    688  * qdbresult->get_str(): Get the result as string by field name
    +
    689  *
    +
    690  * @param result a pointer of qdbresult_t
    +
    691  * @param field column name
    +
    692  *
    +
    693  * @return a string pointer if successful, otherwise returns NULL.
    +
    694  *
    +
    695  * @note
    +
    696  * Do not free returned string.
    +
    697  */
    +
    698 static const char *_resultGetStr(qdbresult_t *result, const char *field)
    +
    699 {
    +
    700 #ifdef Q_ENABLE_MYSQL
    +
    701  if (result == NULL || result->rs == NULL || result->cols <= 0) return NULL;
    +
    702 
    +
    703  if (result->fields == NULL) result->fields = mysql_fetch_fields(result->rs);
    +
    704 
    +
    705  int i;
    +
    706  for (i = 0; i < result->cols; i++) {
    +
    707  if (!strcasecmp(result->fields[i].name, field)) {
    +
    708  return result->get_str_at(result, i + 1);
    +
    709  }
    +
    710  }
    +
    711 
    +
    712  return NULL;
    +
    713 #else
    +
    714  return NULL;
    +
    715 #endif
    +
    716 }
    +
    717 
    +
    718 /**
    +
    719  * qdbresult->get_str_at(): Get the result as string by column number
    +
    720  *
    +
    721  * @param result a pointer of qdbresult_t
    +
    722  * @param idx column number (first column is 1)
    +
    723  *
    +
    724  * @return a string pointer if successful, otherwise returns NULL.
    +
    725  */
    +
    726 static const char *_resultGetStrAt(qdbresult_t *result, int idx)
    +
    727 {
    +
    728 #ifdef Q_ENABLE_MYSQL
    +
    729  if (result == NULL
    +
    730  || result->rs == NULL
    +
    731  || result->cursor == 0
    +
    732  || idx <= 0
    +
    733  || idx > result->cols ) {
    +
    734  return NULL;
    +
    735  }
    +
    736  return result->row[idx-1];
    +
    737 #else
    +
    738  return NULL;
    +
    739 #endif
    +
    740 }
    +
    741 
    +
    742 /**
    +
    743  * qdbresult->get_int(): Get the result as integer by field name
    +
    744  *
    +
    745  * @param result a pointer of qdbresult_t
    +
    746  * @param field column name
    +
    747  *
    +
    748  * @return a integer converted value
    +
    749  */
    +
    750 static int _resultGetInt(qdbresult_t *result, const char *field)
    +
    751 {
    +
    752  const char *val = result->get_str(result, field);
    +
    753  if (val == NULL) return 0;
    +
    754  return atoi(val);
    +
    755 }
    +
    756 
    +
    757 /**
    +
    758  * qdbresult->get_int_at(): Get the result as integer by column number
    +
    759  *
    +
    760  * @param result a pointer of qdbresult_t
    +
    761  * @param idx column number (first column is 1)
    +
    762  *
    +
    763  * @return a integer converted value
    +
    764  */
    +
    765 static int _resultGetIntAt(qdbresult_t *result, int idx)
    +
    766 {
    +
    767  const char *val = result->get_str_at(result, idx);
    +
    768  if (val == NULL) return 0;
    +
    769  return atoi(val);
    +
    770 }
    +
    771 
    +
    772 /**
    +
    773  * qdbresult->get_next(): Retrieves the next row of a result set
    +
    774  *
    +
    775  * @param result a pointer of qdbresult_t
    +
    776  *
    +
    777  * @return true if successful, false if no more rows are left
    +
    778  */
    +
    779 static bool _resultGetNext(qdbresult_t *result)
    +
    780 {
    +
    781 #ifdef Q_ENABLE_MYSQL
    +
    782  if (result == NULL || result->rs == NULL) return false;
    +
    783 
    +
    784  if ((result->row = mysql_fetch_row(result->rs)) == NULL) return false;
    +
    785  result->cursor++;
    +
    786 
    +
    787  return true;
    +
    788 #else
    +
    789  return false;
    +
    790 #endif
    +
    791 }
    +
    792 
    +
    793 /**
    +
    794  * qdbresult->get_cols(): Get the number of columns in the result set
    +
    795  *
    +
    796  * @param result a pointer of qdbresult_t
    +
    797  *
    +
    798  * @return the number of columns in the result set
    +
    799  */
    +
    800 static int result_get_cols(qdbresult_t *result)
    +
    801 {
    +
    802 #ifdef Q_ENABLE_MYSQL
    +
    803  if (result == NULL || result->rs == NULL) return 0;
    +
    804  return result->cols;
    +
    805 #else
    +
    806  return 0;
    +
    807 #endif
    +
    808 }
    +
    809 
    +
    810 /**
    +
    811  * qdbresult->get_rows(): Get the number of rows in the result set
    +
    812  *
    +
    813  * @param result a pointer of qdbresult_t
    +
    814  *
    +
    815  * @return the number of rows in the result set
    +
    816  */
    +
    817 static int result_get_rows(qdbresult_t *result)
    +
    818 {
    +
    819 #ifdef Q_ENABLE_MYSQL
    +
    820  if (result == NULL || result->rs == NULL) return 0;
    +
    821  return mysql_num_rows(result->rs);
    +
    822 #else
    +
    823  return 0;
    +
    824 #endif
    +
    825 }
    +
    826 
    +
    827 /**
    +
    828  * qdbresult->get_row(): Get the current row number
    +
    829  *
    +
    830  * @param result a pointer of qdbresult_t
    +
    831  *
    +
    832  * @return current fetching row number of the result set
    +
    833  *
    +
    834  * @note
    +
    835  * This number is sequencial counter which is started from 1.
    +
    836  */
    +
    837 static int result_get_row(qdbresult_t *result)
    +
    838 {
    +
    839 #ifdef Q_ENABLE_MYSQL
    +
    840  if (result == NULL || result->rs == NULL) return 0;
    +
    841  return result->cursor;
    +
    842 #else
    +
    843  return 0;
    +
    844 #endif
    +
    845 }
    +
    846 
    +
    847 /**
    +
    848  * qdbresult->free(): De-allocate the result
    +
    849  *
    +
    850  * @param result a pointer of qdbresult_t
    +
    851  */
    +
    852 static void result_free(qdbresult_t *result)
    +
    853 {
    +
    854 #ifdef Q_ENABLE_MYSQL
    +
    855  if (result == NULL) return;
    +
    856  if (result->rs != NULL) {
    +
    857  if (result->fetchtype == true) {
    +
    858  while (mysql_fetch_row(result->rs) != NULL);
    +
    859  }
    +
    860  mysql_free_result(result->rs);
    +
    861  result->rs = NULL;
    +
    862  }
    +
    863  free(result);
    +
    864  return;
    +
    865 #else
    +
    866  return;
    +
    867 #endif
    +
    868 }
    +
    869 
    +
    870 #endif
    +
    871 
    +
    872 #endif /* DISABLE_QDATABASE */
    +
    static bool get_conn_status(qdb_t *db)
    qdb->get_conn_status(): Get last connection status
    +
    static const char * get_error(qdb_t *db, unsigned int *errorno)
    qdb->get_error(): Get error number and message
    +
    static int result_get_rows(qdbresult_t *result)
    qdbresult->get_rows(): Get the number of rows in the result set
    +
    static bool begin_tran(qdb_t *db)
    qdb->begin_tran(): Start transaction
    +
    static bool commit(qdb_t *db)
    qdb->commit(): Commit transaction
    +
    static int result_get_row(qdbresult_t *result)
    qdbresult->get_row(): Get the current row number
    +
    static const char * _resultGetStr(qdbresult_t *result, const char *field)
    qdbresult->get_str(): Get the result as string by field name
    +
    static int execute_update(qdb_t *db, const char *query)
    qdb->execute_update(): Executes the update DML
    +
    static void result_free(qdbresult_t *result)
    qdbresult->free(): De-allocate the result
    +
    static bool set_fetchtype(qdb_t *db, bool fromdb)
    qdb->set_fetchtype(): Set result fetching type
    +
    static int _resultGetIntAt(qdbresult_t *result, int idx)
    qdbresult->get_int_at(): Get the result as integer by column number
    +
    static bool rollback(qdb_t *db)
    qdb->rellback(): Roll-back and abort transaction
    +
    static qdbresult_t * execute_queryf(qdb_t *db, const char *format,...)
    qdb->execute_queryf(): Executes the formatted query
    +
    qdb_t * qdb_mysql(const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
    Initialize internal connector structure.
    +
    static bool close_(qdb_t *db)
    qdb->close(): Disconnect from database server
    +
    static int result_get_cols(qdbresult_t *result)
    qdbresult->get_cols(): Get the number of columns in the result set
    +
    static bool ping(qdb_t *db)
    qdb->ping(): Checks whether the connection to the server is working.
    +
    static int execute_updatef(qdb_t *db, const char *format,...)
    qdb->execute_updatef(): Executes the formatted update DML
    +
    static int _resultGetInt(qdbresult_t *result, const char *field)
    qdbresult->get_int(): Get the result as integer by field name
    +
    static qdbresult_t * execute_query(qdb_t *db, const char *query)
    qdb->execute_query(): Executes the query
    +
    static const char * _resultGetStrAt(qdbresult_t *result, int idx)
    qdbresult->get_str_at(): Get the result as string by column number
    +
    static bool open_(qdb_t *db)
    qdb->open(): Connect to database server
    +
    static bool _resultGetNext(qdbresult_t *result)
    qdbresult->get_next(): Retrieves the next row of a result set
    +
    static void free_(qdb_t *db)
    qdb->free(): De-allocate qdb_t structure
    +
    +
    + + + + diff --git a/doc/html/qdatabase__pgsql_8c.html b/doc/html/qdatabase__pgsql_8c.html new file mode 100644 index 00000000..41ff84f6 --- /dev/null +++ b/doc/html/qdatabase__pgsql_8c.html @@ -0,0 +1,1271 @@ + + + + + + + +qLibc: extensions/qdatabase_pgsql.c File Reference + + + + + + + + + + +
    +
    + + + + + + +
    +
    qLibc +
    +
    +
    + + + + + + +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    qdatabase_pgsql.c File Reference
    +
    +
    + +

    Database wrapper. +More...

    + +

    Go to the source code of this file.

    + + + + +

    +Macros

    +#define qtype_cast(_type, _src)   ((_type)_src)
     
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +Functions

    qdb_t * qdb_pgsql (const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
     Initialize internal connector structure. More...
     
    static bool open_ (qdb_t *db)
     qdb->open(): Connect to database server More...
     
    static bool close_ (qdb_t *db)
     qdb->close(): Disconnect from database server More...
     
    static int execute_update (qdb_t *db, const char *query)
     qdb->execute_update(): Executes the update DML More...
     
    static int execute_updatef (qdb_t *db, const char *format,...)
     qdb->execute_updatef(): Executes the formatted update DML More...
     
    static qdbresult_t * execute_query (qdb_t *db, const char *query)
     qdb->execute_query(): Executes the query More...
     
    static qdbresult_t * execute_queryf (qdb_t *db, const char *format,...)
     qdb->execute_queryf(): Executes the formatted query More...
     
    static bool begin_tran (qdb_t *db)
     qdb->begin_tran(): Start transaction More...
     
    static bool commit (qdb_t *db)
     qdb->commit(): Commit transaction More...
     
    static bool rollback (qdb_t *db)
     qdb->rellback(): Roll-back and abort transaction More...
     
    static bool set_fetchtype (qdb_t *db, bool fromdb)
     qdb->set_fetchtype(): Set result fetching type More...
     
    static bool get_conn_status (qdb_t *db)
     qdb->get_conn_status(): Get last connection status More...
     
    static bool ping (qdb_t *db)
     qdb->ping(): Checks whether the connection to the server is working. More...
     
    static const char * get_error (qdb_t *db, unsigned int *errorno)
     qdb->get_error(): Get error number and message More...
     
    static void free_ (qdb_t *db)
     qdb->free(): De-allocate qdb_t structure More...
     
    static const char * _resultGetStr (qdbresult_t *result, const char *field)
     qdbresult->get_str(): Get the result as string by field name More...
     
    static const char * _resultGetStrAt (qdbresult_t *result, int idx)
     qdbresult->get_str_at(): Get the result as string by column number More...
     
    static int _resultGetInt (qdbresult_t *result, const char *field)
     qdbresult->get_int(): Get the result as integer by field name More...
     
    static int _resultGetIntAt (qdbresult_t *result, int idx)
     qdbresult->get_int_at(): Get the result as integer by column number More...
     
    static bool _resultGetNext (qdbresult_t *result)
     qdbresult->get_next(): Retrieves the next row of a result set More...
     
    static int result_get_cols (qdbresult_t *result)
     qdbresult->get_cols(): Get the number of columns in the result set More...
     
    static int result_get_rows (qdbresult_t *result)
     qdbresult->get_rows(): Get the number of rows in the result set More...
     
    static int result_get_row (qdbresult_t *result)
     qdbresult->get_row(): Get the current row number More...
     
    static void result_free (qdbresult_t *result)
     qdbresult->free(): De-allocate the result More...
     
    +

    Detailed Description

    +

    Database wrapper.

    +

    Database header files should be included prior to qlibcext.h in your source codes like below.

    +
    #include "libpq-fe.h"
    +
    #include "qlibcext.h"
    +
    qlibc extension header file.
    +
    qdb_t *db = NULL;
    +
    qdbresult_t *result = NULL;
    +
    +
    db = qdb("PGSQL", "127.0.0.1", 5432,
    +
    "test", "secret", "sampledb", true);
    +
    if (db == NULL) {
    +
    printf("ERROR: Not supported database type.\n");
    +
    return -1;
    +
    }
    +
    +
    // try to connect
    +
    if (db->open(db) == false) {
    +
    printf("WARNING: Can't connect to database.\n");
    +
    return -1;
    +
    }
    +
    +
    // get results
    +
    result = db->execute_query(db, "SELECT name, population FROM City");
    +
    if (result != NULL) {
    +
    printf("COLS : %d , ROWS : %d\n",
    +
    result->get_cols(result), result->get_rows(result));
    +
    while (result->get_next(result) == true) {
    +
    char *pszName = result->get_str(result, "name");
    +
    int nPopulation = result->get_int(result, "population");
    +
    printf("Country : %s , Population : %d\n", pszName, nPopulation);
    +
    }
    +
    result->free(result);
    +
    }
    +
    +
    // close connection
    +
    db->close(db);
    +
    +
    // free db object
    +
    db->free(db);
    +
    +

    Definition in file qdatabase_pgsql.c.

    +

    Function Documentation

    + +

    ◆ qdb_pgsql()

    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    qdb_t* qdb_pgsql (const char * dbtype,
    const char * addr,
    int port,
    const char * database,
    const char * username,
    const char * password,
    bool autocommit 
    )
    +
    + +

    Initialize internal connector structure.

    +
    Parameters
    + + + + + + + + +
    dbtypedatabase server type. currently "PGSQL" is only supported
    addrip or fqdn address.
    portport number
    usernamedatabase username
    passworddatabase password
    databasedatabase server type. currently "PGSQL" is only supported
    autocommitcan only be set to true, when dbtype is "PGSQL"
    +
    +
    +
    Returns
    a pointer of qdb_t object in case of successful, otherwise returns NULL.
    +
    qdb_t *db = qdb_pgsql("PGSQL", "127.0.0.1", 5432,
    +
    "test", "secret", "sampledb", true);
    +
    if (db == NULL) {
    +
    printf("ERROR: Not supported database type.\n");
    +
    return -1;
    +
    }
    +
    qdb_t * qdb_pgsql(const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
    Initialize internal connector structure.
    +
    +

    Definition at line 312 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ open_()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool open_ (qdb_t * db)
    +
    +static
    +
    + +

    qdb->open(): Connect to database server

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if successful, otherwise returns false.
    + +

    Definition at line 383 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ close_()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool close_ (qdb_t * db)
    +
    +static
    +
    + +

    qdb->close(): Disconnect from database server

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if successful, otherwise returns false.
    +
    Note
    Unless you call qdb->free(), qdb_t object will keep the database information. So you can re-connect to database using qdb->open().
    + +

    Definition at line 464 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ execute_update()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static int execute_update (qdb_t * db,
    const char * query 
    )
    +
    +static
    +
    + +

    qdb->execute_update(): Executes the update DML

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    queryquery string
    +
    +
    +
    Returns
    a number of affected rows
    + +

    Definition at line 492 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ execute_updatef()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static int execute_updatef (qdb_t * db,
    const char * format,
     ... 
    )
    +
    +static
    +
    + +

    qdb->execute_updatef(): Executes the formatted update DML

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    formatquery string format
    +
    +
    +
    Returns
    a number of affected rows, otherwise returns -1
    + +

    Definition at line 523 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ execute_query()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static qdbresult_t* execute_query (qdb_t * db,
    const char * query 
    )
    +
    +static
    +
    + +

    qdb->execute_query(): Executes the query

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    queryquery string
    +
    +
    +
    Returns
    a pointer of qdbresult_t if successful, otherwise returns NULL
    + +

    Definition at line 543 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ execute_queryf()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    static qdbresult_t* execute_queryf (qdb_t * db,
    const char * format,
     ... 
    )
    +
    +static
    +
    + +

    qdb->execute_queryf(): Executes the formatted query

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    formatquery string format
    +
    +
    +
    Returns
    a pointer of qdbresult_t if successful, otherwise returns NULL
    + +

    Definition at line 590 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ begin_tran()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool begin_tran (qdb_t * db)
    +
    +static
    +
    + +

    qdb->begin_tran(): Start transaction

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if successful, otherwise returns false
    +
    db->begin_tran(db);
    +
    (... insert/update/delete ...)
    +
    db->commit(db);
    +
    +

    Definition at line 614 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ commit()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool commit (qdb_t * db)
    +
    +static
    +
    + +

    qdb->commit(): Commit transaction

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if successful, otherwise returns false
    + +

    Definition at line 642 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ rollback()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool rollback (qdb_t * db)
    +
    +static
    +
    + +

    qdb->rellback(): Roll-back and abort transaction

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if successful, otherwise returns false
    + +

    Definition at line 668 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ set_fetchtype()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static bool set_fetchtype (qdb_t * db,
    bool fromdb 
    )
    +
    +static
    +
    + +

    qdb->set_fetchtype(): Set result fetching type

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    fromdbfalse for storing the results to client (default mode), true for fetching directly from server,
    +
    +
    +
    Returns
    true if successful otherwise returns false
    +
    Note
    If qdb->set_fetchtype(db, true) is called, the results does not actually read into the client. Instead, each row must be retrieved individually by making calls to qdbresult->get_next(). This reads the result of a query directly from the server without storing it in local buffer, which is somewhat faster and uses much less memory than default behavior qdb->set_fetchtype(db, false).
    + +

    Definition at line 704 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ get_conn_status()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool get_conn_status (qdb_t * db)
    +
    +static
    +
    + +

    qdb->get_conn_status(): Get last connection status

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if the connection flag is set to alive, otherwise returns false
    +
    Note
    This function just returns the the connection status flag.
    + +

    Definition at line 725 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ ping()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool ping (qdb_t * db)
    +
    +static
    +
    + +

    qdb->ping(): Checks whether the connection to the server is working.

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    +
    Returns
    true if connection is alive, false if connection is not available and failed to reconnect
    +
    Note
    If the connection has gone down, an attempt to reconnect.
    + +

    Definition at line 743 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ get_error()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static const char* get_error (qdb_t * db,
    unsigned int * errorno 
    )
    +
    +static
    +
    + +

    qdb->get_error(): Get error number and message

    +
    Parameters
    + + + +
    dba pointer of qdb_t object
    errornoif not NULL, error number will be stored
    +
    +
    +
    Returns
    a pointer of error message string
    +
    Note
    Do not free returned error message
    + +

    Definition at line 774 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ free_()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static void free_ (qdb_t * db)
    +
    +static
    +
    + +

    qdb->free(): De-allocate qdb_t structure

    +
    Parameters
    + + +
    dba pointer of qdb_t object
    +
    +
    + +

    Definition at line 804 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ _resultGetStr()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static const char* _resultGetStr (qdbresult_t * result,
    const char * field 
    )
    +
    +static
    +
    + +

    qdbresult->get_str(): Get the result as string by field name

    +
    Parameters
    + + + +
    resulta pointer of qdbresult_t
    fieldcolumn name
    +
    +
    +
    Returns
    a string pointer if successful, otherwise returns NULL.
    +
    Note
    Do not free returned string.
    + +

    Definition at line 836 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ _resultGetStrAt()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static const char* _resultGetStrAt (qdbresult_t * result,
    int idx 
    )
    +
    +static
    +
    + +

    qdbresult->get_str_at(): Get the result as string by column number

    +
    Parameters
    + + + +
    resulta pointer of qdbresult_t
    idxcolumn number (first column is 1)
    +
    +
    +
    Returns
    a string pointer if successful, otherwise returns NULL.
    + +

    Definition at line 879 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ _resultGetInt()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static int _resultGetInt (qdbresult_t * result,
    const char * field 
    )
    +
    +static
    +
    + +

    qdbresult->get_int(): Get the result as integer by field name

    +
    Parameters
    + + + +
    resulta pointer of qdbresult_t
    fieldcolumn name
    +
    +
    +
    Returns
    a integer converted value
    + +

    Definition at line 907 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ _resultGetIntAt()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    static int _resultGetIntAt (qdbresult_t * result,
    int idx 
    )
    +
    +static
    +
    + +

    qdbresult->get_int_at(): Get the result as integer by column number

    +
    Parameters
    + + + +
    resulta pointer of qdbresult_t
    idxcolumn number (first column is 1)
    +
    +
    +
    Returns
    a integer converted value
    + +

    Definition at line 922 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ _resultGetNext()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static bool _resultGetNext (qdbresult_t * result)
    +
    +static
    +
    + +

    qdbresult->get_next(): Retrieves the next row of a result set

    +
    Parameters
    + + +
    resulta pointer of qdbresult_t
    +
    +
    +
    Returns
    true if successful, false if no more rows are left
    + +

    Definition at line 936 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ result_get_cols()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static int result_get_cols (qdbresult_t * result)
    +
    +static
    +
    + +

    qdbresult->get_cols(): Get the number of columns in the result set

    +
    Parameters
    + + +
    resulta pointer of qdbresult_t
    +
    +
    +
    Returns
    the number of columns in the result set
    + +

    Definition at line 964 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ result_get_rows()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static int result_get_rows (qdbresult_t * result)
    +
    +static
    +
    + +

    qdbresult->get_rows(): Get the number of rows in the result set

    +
    Parameters
    + + +
    resulta pointer of qdbresult_t
    +
    +
    +
    Returns
    the number of rows in the result set
    + +

    Definition at line 981 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ result_get_row()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static int result_get_row (qdbresult_t * result)
    +
    +static
    +
    + +

    qdbresult->get_row(): Get the current row number

    +
    Parameters
    + + +
    resulta pointer of qdbresult_t
    +
    +
    +
    Returns
    current fetching row number of the result set
    +
    Note
    This number is sequencial counter which is started from 1.
    + +

    Definition at line 1001 of file qdatabase_pgsql.c.

    + +
    +
    + +

    ◆ result_free()

    + +
    +
    + + + + + +
    + + + + + + + + +
    static void result_free (qdbresult_t * result)
    +
    +static
    +
    + +

    qdbresult->free(): De-allocate the result

    +
    Parameters
    + + +
    resulta pointer of qdbresult_t
    +
    +
    + +

    Definition at line 1016 of file qdatabase_pgsql.c.

    + +
    +
    +
    +
    + + + + diff --git a/doc/html/qdatabase__pgsql_8c.js b/doc/html/qdatabase__pgsql_8c.js new file mode 100644 index 00000000..1c54bf5e --- /dev/null +++ b/doc/html/qdatabase__pgsql_8c.js @@ -0,0 +1,28 @@ +var qdatabase__pgsql_8c = +[ + [ "qtype_cast", "qdatabase__pgsql_8c.html#a6779bf4ee99e4970276be7a408fce3bc", null ], + [ "qdb_pgsql", "qdatabase__pgsql_8c.html#adc1ca8409ff2e5d1d688600dc2c3b54d", null ], + [ "open_", "qdatabase__pgsql_8c.html#ae90a2206952d27df4a58995628741134", null ], + [ "close_", "qdatabase__pgsql_8c.html#a89fc888110ad23e6358d155e2c9b6a3b", null ], + [ "execute_update", "qdatabase__pgsql_8c.html#a42069ce53c9e45b7283e9e44a7e021d9", null ], + [ "execute_updatef", "qdatabase__pgsql_8c.html#a99e372b7dd8e2d9c2bb4f762cc8dd5aa", null ], + [ "execute_query", "qdatabase__pgsql_8c.html#abe4e5330b68b3fc9ff94b086939dba82", null ], + [ "execute_queryf", "qdatabase__pgsql_8c.html#a6fde99c74f91137ac554244ac60344be", null ], + [ "begin_tran", "qdatabase__pgsql_8c.html#a2f27adbcda036187a4714e5bcd3a247e", null ], + [ "commit", "qdatabase__pgsql_8c.html#a3386b5894d03e2f48deecb0b7c0632ea", null ], + [ "rollback", "qdatabase__pgsql_8c.html#a6ee4a69fbc4ecf80e795d6b6b3750ca7", null ], + [ "set_fetchtype", "qdatabase__pgsql_8c.html#a5dc988c35746fe8a792702523abc66b3", null ], + [ "get_conn_status", "qdatabase__pgsql_8c.html#a0598e067ed6b749c9df042cd7bc83f5c", null ], + [ "ping", "qdatabase__pgsql_8c.html#a998f1143e4b00ff847f120e34a0efc0d", null ], + [ "get_error", "qdatabase__pgsql_8c.html#a11f5b1c5de493c27e1d396f7593408a0", null ], + [ "free_", "qdatabase__pgsql_8c.html#afe5578f18ea12e68e96a80b91bd9be9d", null ], + [ "_resultGetStr", "qdatabase__pgsql_8c.html#a3c32ab62afb92fb6a55cfaff7c92bf42", null ], + [ "_resultGetStrAt", "qdatabase__pgsql_8c.html#ae3559441aaa0ae5f19965612dccca8d5", null ], + [ "_resultGetInt", "qdatabase__pgsql_8c.html#aa7608c9e6913b4d3c71d62cd43073fd1", null ], + [ "_resultGetIntAt", "qdatabase__pgsql_8c.html#a6b5beda54ba78647276bc180f65138ca", null ], + [ "_resultGetNext", "qdatabase__pgsql_8c.html#aeb86d8858bb8ea5dd06e052f8f3e9a09", null ], + [ "result_get_cols", "qdatabase__pgsql_8c.html#a95d88451db51492ae6122701ce56b94d", null ], + [ "result_get_rows", "qdatabase__pgsql_8c.html#a26f8b55348c57f467d64c002ab306ff6", null ], + [ "result_get_row", "qdatabase__pgsql_8c.html#a396a7328f7fb499f89c2652598185dc8", null ], + [ "result_free", "qdatabase__pgsql_8c.html#a4ab66a07fa82b706895e1b2c19fcd501", null ] +]; \ No newline at end of file diff --git a/doc/html/qdatabase__pgsql_8c_source.html b/doc/html/qdatabase__pgsql_8c_source.html new file mode 100644 index 00000000..d5e5eb69 --- /dev/null +++ b/doc/html/qdatabase__pgsql_8c_source.html @@ -0,0 +1,1130 @@ + + + + + + + +qLibc: extensions/qdatabase_pgsql.c Source File + + + + + + + + + + +
    +
    + + + + + + +
    +
    qLibc +
    +
    +
    + + + + + + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    qdatabase_pgsql.c
    +
    +
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2024 SunBeau.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qdatabase_pgsql.c Database wrapper.
    +
    31  *
    +
    32  * Database header files should be included prior to qlibcext.h in your source
    +
    33  * codes like below.
    +
    34  *
    +
    35  * @code
    +
    36  * #include "libpq-fe.h"
    +
    37  * #include "qlibcext.h"
    +
    38  * @endcode
    +
    39  *
    +
    40  * @code
    +
    41  * qdb_t *db = NULL;
    +
    42  * qdbresult_t *result = NULL;
    +
    43  *
    +
    44  * db = qdb("PGSQL", "127.0.0.1", 5432,
    +
    45  * "test", "secret", "sampledb", true);
    +
    46  * if (db == NULL) {
    +
    47  * printf("ERROR: Not supported database type.\n");
    +
    48  * return -1;
    +
    49  * }
    +
    50  *
    +
    51  * // try to connect
    +
    52  * if (db->open(db) == false) {
    +
    53  * printf("WARNING: Can't connect to database.\n");
    +
    54  * return -1;
    +
    55  * }
    +
    56  *
    +
    57  * // get results
    +
    58  * result = db->execute_query(db, "SELECT name, population FROM City");
    +
    59  * if (result != NULL) {
    +
    60  * printf("COLS : %d , ROWS : %d\n",
    +
    61  * result->get_cols(result), result->get_rows(result));
    +
    62  * while (result->get_next(result) == true) {
    +
    63  * char *pszName = result->get_str(result, "name");
    +
    64  * int nPopulation = result->get_int(result, "population");
    +
    65  * printf("Country : %s , Population : %d\n", pszName, nPopulation);
    +
    66  * }
    +
    67  * result->free(result);
    +
    68  * }
    +
    69  *
    +
    70  * // close connection
    +
    71  * db->close(db);
    +
    72  *
    +
    73  * // free db object
    +
    74  * db->free(db);
    +
    75  * @endcode
    +
    76  */
    +
    77 
    +
    78 #ifndef DISABLE_QDATABASE
    +
    79 
    +
    80 #if defined(ENABLE_PGSQL) || defined( _DOXYGEN_SKIP)
    +
    81 
    +
    82 #define qtype_cast(_type, _src) ((_type)_src)
    +
    83 
    +
    84 #ifdef ENABLE_PGSQL
    +
    85 #include "libpq-fe.h"
    +
    86 /* pgsql specific connector options */
    +
    87 #define Q_PGSQL_OPT_RECONNECT (1)
    +
    88 #define Q_PGSQL_OPT_CONNECT_TIMEOUT (10)
    +
    89 #define Q_PGSQL_OPT_READ_TIMEOUT (30)
    +
    90 #define Q_PGSQL_OPT_WRITE_TIMEOUT (30)
    +
    91 #endif
    +
    92 
    +
    93 #include <stdio.h>
    +
    94 #include <stdlib.h>
    +
    95 #include <stdbool.h>
    +
    96 #include <stdarg.h>
    +
    97 #include <string.h>
    +
    98 
    +
    99 #include "qinternal.h"
    +
    100 #include "extensions/qdatabase.h"
    +
    101 
    +
    102 /*
    +
    103  * Member method protos
    +
    104  */
    +
    105 #ifndef _DOXYGEN_SKIP
    +
    106 // qdb_t object
    +
    107 static bool open_(qdb_t *db);
    +
    108 static bool close_(qdb_t *db);
    +
    109 
    +
    110 static int execute_update(qdb_t *db, const char *query);
    +
    111 static int execute_updatef(qdb_t *db, const char *format, ...);
    +
    112 static qdbresult_t *execute_query(qdb_t *db, const char *query);
    +
    113 static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...);
    +
    114 
    +
    115 static bool begin_tran(qdb_t *db);
    +
    116 static bool commit(qdb_t *db);
    +
    117 static bool rollback(qdb_t *db);
    +
    118 
    +
    119 static bool set_fetchtype(qdb_t *db, bool use);
    +
    120 static bool get_conn_status(qdb_t *db);
    +
    121 static bool ping(qdb_t *db);
    +
    122 static const char *get_error(qdb_t *db, unsigned int *errorno);
    +
    123 static void free_(qdb_t *db);
    +
    124 
    +
    125 // qdbresult_t object
    +
    126 static const char *_resultGetStr(qdbresult_t *result, const char *field);
    +
    127 static const char *_resultGetStrAt(qdbresult_t *result, int idx);
    +
    128 static int _resultGetInt(qdbresult_t *result, const char *field);
    +
    129 static int _resultGetIntAt(qdbresult_t *result, int idx);
    +
    130 static bool _resultGetNext(qdbresult_t *result);
    +
    131 
    +
    132 static int result_get_cols(qdbresult_t *result);
    +
    133 static int result_get_rows(qdbresult_t *result);
    +
    134 static int result_get_row(qdbresult_t *result);
    +
    135 
    +
    136 static void result_free(qdbresult_t *result);
    +
    137 
    +
    138 #endif
    +
    139 
    +
    140 /*
    +
    141  * private for pgsql database
    +
    142  */
    +
    143 #ifndef _DOXYGEN_SKIP
    +
    144 typedef enum {
    +
    145  PQ_UNKNOWN,
    +
    146  PQ_WRITE,
    +
    147  PQ_READ,
    +
    148  PQ_PING,
    +
    149 } pgquery_t;
    +
    150 
    +
    151 typedef struct {
    +
    152  char *emsg;
    +
    153  PGconn *pgconn;
    +
    154 
    +
    155  PGresult *pgresult;
    +
    156  int rows;
    +
    157  int cols;
    +
    158  int cursor;
    +
    159 } pgsql_t;
    +
    160 
    +
    161 static inline pgsql_t* pgsql_init(void)
    +
    162 {
    +
    163  pgsql_t* pgsql = (pgsql_t *)calloc(1, sizeof(pgsql_t));
    +
    164  return pgsql;
    +
    165 }
    +
    166 
    +
    167 static inline const char* pgsql_set_emsg(pgsql_t* pgsql, const char* emsg)
    +
    168 {
    +
    169  if (pgsql == NULL || emsg == NULL) {
    +
    170  return NULL;
    +
    171  }
    +
    172 
    +
    173  if (pgsql->emsg) {
    +
    174  free(pgsql->emsg);
    +
    175  }
    +
    176 
    +
    177  pgsql->emsg = strdup(emsg);
    +
    178  return pgsql->emsg;
    +
    179 }
    +
    180 
    +
    181 static inline void pgsql_close(pgsql_t* pgsql)
    +
    182 {
    +
    183  if (pgsql == NULL) {
    +
    184  return;
    +
    185  }
    +
    186 
    +
    187  if (pgsql->emsg) {
    +
    188  free(pgsql->emsg);
    +
    189  pgsql->emsg = NULL;
    +
    190  }
    +
    191 
    +
    192  if (pgsql->pgresult) {
    +
    193  PQclear(pgsql->pgresult);
    +
    194  pgsql->pgresult = NULL;
    +
    195  }
    +
    196 
    +
    197  if (pgsql->pgconn) {
    +
    198  PQfinish(pgsql->pgconn);
    +
    199  pgsql->pgconn = NULL;
    +
    200  }
    +
    201 
    +
    202  free(pgsql);
    +
    203 }
    +
    204 
    +
    205 static inline void pgsql_query_result(pgsql_t* pgsql)
    +
    206 {
    +
    207  if (pgsql->pgresult) {
    +
    208  PQclear(pgsql->pgresult);
    +
    209  pgsql->pgresult = NULL;
    +
    210  }
    +
    211 }
    +
    212 
    +
    213 static inline int pgsql_query(pgsql_t* pgsql, const char *query, pgquery_t type)
    +
    214 {
    +
    215  if (pgsql == NULL || pgsql->pgconn == NULL) {
    +
    216  return -1; /* error */
    +
    217  }
    +
    218 
    +
    219  /* free previous query results */
    +
    220  pgsql_query_result(pgsql);
    +
    221 
    +
    222  if (type == PQ_PING) {
    +
    223  query = "";
    +
    224  }
    +
    225 
    +
    226  PGresult *result = PQexec(pgsql->pgconn, query);
    +
    227  ExecStatusType status = PQresultStatus(result);
    +
    228 
    +
    229  switch (type) {
    +
    230  case PQ_PING: {
    +
    231  if (status != PGRES_EMPTY_QUERY) {
    +
    232  return -1; /* error */
    +
    233  }
    +
    234  break;
    +
    235  }
    +
    236 
    +
    237  case PQ_READ: {
    +
    238  if (status != PGRES_TUPLES_OK) {
    +
    239  return -1; /* error */
    +
    240  }
    +
    241  break;
    +
    242  }
    +
    243 
    +
    244  default: {
    +
    245  if (status != PGRES_COMMAND_OK) {
    +
    246  return -1; /* error */
    +
    247  }
    +
    248  break;
    +
    249  }
    +
    250  }
    +
    251 
    +
    252  pgsql->pgresult = result;
    +
    253 
    +
    254  return 0; /* ok */
    +
    255 }
    +
    256 
    +
    257 static inline int pgsql_affected_rows(pgsql_t* pgsql)
    +
    258 {
    +
    259  if (pgsql && pgsql->pgresult) {
    +
    260  return atoi(PQcmdTuples(pgsql->pgresult));
    +
    261  }
    +
    262 
    +
    263  /* error */
    +
    264  return -1;
    +
    265 }
    +
    266 
    +
    267 static inline int pgsql_num_rows(pgsql_t* pgsql)
    +
    268 {
    +
    269  if (pgsql && pgsql->pgresult) {
    +
    270  return PQntuples(pgsql->pgresult);
    +
    271  }
    +
    272 
    +
    273  /* error */
    +
    274  return -1;
    +
    275 }
    +
    276 
    +
    277 static inline int pgsql_num_fields(pgsql_t* pgsql)
    +
    278 {
    +
    279  if (pgsql && pgsql->pgresult) {
    +
    280  return PQnfields(pgsql->pgresult);
    +
    281  }
    +
    282 
    +
    283  /* error */
    +
    284  return -1;
    +
    285 }
    +
    286 
    +
    287 #endif /* _DOXYGEN_SKIP */
    +
    288 
    +
    289 /**
    +
    290  * Initialize internal connector structure
    +
    291  *
    +
    292  * @param dbtype database server type. currently "PGSQL" is only supported
    +
    293  * @param addr ip or fqdn address.
    +
    294  * @param port port number
    +
    295  * @param username database username
    +
    296  * @param password database password
    +
    297  * @param database database server type. currently "PGSQL" is only supported
    +
    298  * @param autocommit can only be set to true, when dbtype is "PGSQL"
    +
    299  *
    +
    300  * @return a pointer of qdb_t object in case of successful,
    +
    301  * otherwise returns NULL.
    +
    302  *
    +
    303  * @code
    +
    304  * qdb_t *db = qdb_pgsql("PGSQL", "127.0.0.1", 5432,
    +
    305  * "test", "secret", "sampledb", true);
    +
    306  * if (db == NULL) {
    +
    307  * printf("ERROR: Not supported database type.\n");
    +
    308  * return -1;
    +
    309  * }
    +
    310  * @endcode
    +
    311  */
    +
    312 qdb_t *qdb_pgsql(const char *dbtype, const char *addr, int port, const char *database,
    +
    313  const char *username, const char *password, bool autocommit)
    +
    314 {
    +
    315  // check db type
    +
    316 #ifdef Q_ENABLE_PGSQL
    +
    317  if (strcmp(dbtype, "PGSQL")) return NULL;
    +
    318 #else
    +
    319  return NULL;
    +
    320 #endif
    +
    321  if (dbtype == NULL
    +
    322  || addr == NULL
    +
    323  || username == NULL
    +
    324  || password == NULL
    +
    325  || database == NULL
    +
    326  || autocommit == false) {
    +
    327  return NULL;
    +
    328  }
    +
    329 
    +
    330  // initialize
    +
    331  qdb_t *db;
    +
    332  if ((db = (qdb_t *)malloc(sizeof(qdb_t))) == NULL) return NULL;
    +
    333  memset((void *)db, 0, sizeof(qdb_t));
    +
    334  db->connected = false;
    +
    335 
    +
    336  // set common structure
    +
    337  db->info.dbtype = strdup(dbtype);
    +
    338  db->info.addr = strdup(addr);
    +
    339  db->info.port = port;
    +
    340  db->info.username = strdup(username);
    +
    341  db->info.password = strdup(password);
    +
    342  db->info.database = strdup(database);
    +
    343  db->info.autocommit = autocommit;
    +
    344  db->info.fetchtype = false;// store mode
    +
    345 
    +
    346  // set db handler
    +
    347 #ifdef Q_ENABLE_PGSQL
    +
    348  db->pgsql = NULL;
    +
    349 #endif
    +
    350 
    +
    351  // assign methods
    +
    352  db->open = open_;
    +
    353  db->close = close_;
    +
    354 
    +
    355  db->execute_update = execute_update;
    +
    356  db->execute_updatef = execute_updatef;
    +
    357  db->execute_query = execute_query;
    +
    358  db->execute_queryf = execute_queryf;
    +
    359 
    +
    360  db->begin_tran = begin_tran;
    +
    361  db->commit = commit;
    +
    362  db->rollback = rollback;
    +
    363 
    +
    364  db->set_fetchtype = set_fetchtype;
    +
    365  db->get_conn_status = get_conn_status;
    +
    366  db->ping = ping;
    +
    367  db->get_error = get_error;
    +
    368  db->free = free_;
    +
    369 
    +
    370  // initialize recrusive mutex
    +
    371  Q_MUTEX_NEW(db->qmutex, true);
    +
    372 
    +
    373  return db;
    +
    374 }
    +
    375 
    +
    376 /**
    +
    377  * qdb->open(): Connect to database server
    +
    378  *
    +
    379  * @param db a pointer of qdb_t object
    +
    380  *
    +
    381  * @return true if successful, otherwise returns false.
    +
    382  */
    +
    383 static bool open_(qdb_t *db)
    +
    384 {
    +
    385  if (db == NULL) return false;
    +
    386 
    +
    387  // if connected, close first
    +
    388  if (db->connected == true) {
    +
    389  close_(db);
    +
    390  }
    +
    391 
    +
    392 #ifdef Q_ENABLE_PGSQL
    +
    393  Q_MUTEX_ENTER(db->qmutex);
    +
    394 
    +
    395  bool ret = true;
    +
    396  PGconn* pgconn = NULL;
    +
    397 
    +
    398  // initialize handler
    +
    399  if (db->pgsql != NULL) close_(db);
    +
    400 
    +
    401  if ((db->pgsql = pgsql_init()) == NULL) {
    +
    402  ret = false;
    +
    403  goto gt_quit;
    +
    404  }
    +
    405 
    +
    406  // set options
    +
    407  /* do nothing */
    +
    408 
    +
    409  /* int to char* */
    +
    410  char pgport[10];
    +
    411  snprintf(pgport, sizeof(pgport), "%d", db->info.port);
    +
    412 
    +
    413  // try to connect
    +
    414  pgconn = PQsetdbLogin(db->info.addr, pgport,
    +
    415  NULL, NULL,
    +
    416  db->info.database, db->info.username, db->info.password);
    +
    417  if (pgconn == NULL) {
    +
    418  close_(db); // free pgsql handler
    +
    419  ret = false;
    +
    420  goto gt_quit;
    +
    421  }
    +
    422 
    +
    423  if (PQstatus(pgconn) != CONNECTION_OK) {
    +
    424  const char* emsg = PQerrorMessage(pgconn);
    +
    425  pgsql_set_emsg(db->pgsql, emsg);
    +
    426  // fprintf(stderr, "%s", emsg);
    +
    427  ret = false;
    +
    428  goto gt_quit;
    +
    429  }
    +
    430 
    +
    431  qtype_cast(pgsql_t*, db->pgsql)->pgconn = pgconn;
    +
    432 
    +
    433  // set auto-commit
    +
    434  /* do nothing */
    +
    435 
    +
    436  // set flag
    +
    437  db->connected = true;
    +
    438 
    +
    439 gt_quit:
    +
    440 
    +
    441  if (ret == false) {
    +
    442  PQfinish(pgconn);
    +
    443  }
    +
    444 
    +
    445  Q_MUTEX_LEAVE(db->qmutex);
    +
    446  return ret;
    +
    447 
    +
    448 #else
    +
    449  return false;
    +
    450 #endif
    +
    451 }
    +
    452 
    +
    453 /**
    +
    454  * qdb->close(): Disconnect from database server
    +
    455  *
    +
    456  * @param db a pointer of qdb_t object
    +
    457  *
    +
    458  * @return true if successful, otherwise returns false.
    +
    459  *
    +
    460  * @note
    +
    461  * Unless you call qdb->free(), qdb_t object will keep the database
    +
    462  * information. So you can re-connect to database using qdb->open().
    +
    463  */
    +
    464 static bool close_(qdb_t *db)
    +
    465 {
    +
    466  if (db == NULL) return false;
    +
    467 
    +
    468 #ifdef Q_ENABLE_PGSQL
    +
    469  Q_MUTEX_ENTER(db->qmutex);
    +
    470 
    +
    471  if (db->pgsql != NULL) {
    +
    472  pgsql_close(db->pgsql);
    +
    473  db->pgsql = NULL;
    +
    474  }
    +
    475  db->connected = false;
    +
    476 
    +
    477  Q_MUTEX_LEAVE(db->qmutex);
    +
    478  return true;
    +
    479 #else
    +
    480  return false;
    +
    481 #endif
    +
    482 }
    +
    483 
    +
    484 /**
    +
    485  * qdb->execute_update(): Executes the update DML
    +
    486  *
    +
    487  * @param db a pointer of qdb_t object
    +
    488  * @param query query string
    +
    489  *
    +
    490  * @return a number of affected rows
    +
    491  */
    +
    492 static int execute_update(qdb_t *db, const char *query)
    +
    493 {
    +
    494  if (db == NULL || db->connected == false) return -1;
    +
    495 
    +
    496 #ifdef Q_ENABLE_PGSQL
    +
    497  Q_MUTEX_ENTER(db->qmutex);
    +
    498 
    +
    499  int affected = -1;
    +
    500 
    +
    501  // query
    +
    502  DEBUG("%s", query);
    +
    503  if (pgsql_query(db->pgsql, query, PQ_WRITE) == 0) {
    +
    504  /* get affected rows */
    +
    505  if ((affected = pgsql_affected_rows(db->pgsql)) < 0) affected = -1;
    +
    506  }
    +
    507 
    +
    508  Q_MUTEX_LEAVE(db->qmutex);
    +
    509  return affected;
    +
    510 #else
    +
    511  return -1;
    +
    512 #endif
    +
    513 }
    +
    514 
    +
    515 /**
    +
    516  * qdb->execute_updatef(): Executes the formatted update DML
    +
    517  *
    +
    518  * @param db a pointer of qdb_t object
    +
    519  * @param format query string format
    +
    520  *
    +
    521  * @return a number of affected rows, otherwise returns -1
    +
    522  */
    +
    523 static int execute_updatef(qdb_t *db, const char *format, ...)
    +
    524 {
    +
    525  char *query;
    +
    526  DYNAMIC_VSPRINTF(query, format);
    +
    527  if (query == NULL) return -1;
    +
    528 
    +
    529  int affected = execute_update(db, query);
    +
    530  free(query);
    +
    531 
    +
    532  return affected;
    +
    533 }
    +
    534 
    +
    535 /**
    +
    536  * qdb->execute_query(): Executes the query
    +
    537  *
    +
    538  * @param db a pointer of qdb_t object
    +
    539  * @param query query string
    +
    540  *
    +
    541  * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    +
    542  */
    +
    543 static qdbresult_t *execute_query(qdb_t *db, const char *query)
    +
    544 {
    +
    545  if (db == NULL || db->connected == false) return NULL;
    +
    546 
    +
    547 #ifdef Q_ENABLE_PGSQL
    +
    548  // query
    +
    549  DEBUG("%s", query);
    +
    550  if (pgsql_query(db->pgsql, query, PQ_READ)) return NULL;
    +
    551 
    +
    552  // store
    +
    553  qdbresult_t *result = (qdbresult_t *)malloc(sizeof(qdbresult_t));
    +
    554  if (result == NULL) return NULL;
    +
    555  result->pgsql = db->pgsql;
    +
    556 
    +
    557  /* get meta data */
    +
    558  // result->row = NULL;
    +
    559  qtype_cast(pgsql_t*, result->pgsql)->rows = pgsql_num_rows(result->pgsql);
    +
    560  qtype_cast(pgsql_t*, result->pgsql)->cols = pgsql_num_fields(result->pgsql);
    +
    561  qtype_cast(pgsql_t*, result->pgsql)->cursor = 0;
    +
    562 
    +
    563  /* assign methods */
    +
    564  result->getstr = _resultGetStr;
    +
    565  result->get_str_at = _resultGetStrAt;
    +
    566  result->getint = _resultGetInt;
    +
    567  result->get_int_at = _resultGetIntAt;
    +
    568  result->getnext = _resultGetNext;
    +
    569 
    +
    570  result->get_cols = result_get_cols;
    +
    571  result->get_rows = result_get_rows;
    +
    572  result->get_row = result_get_row;
    +
    573 
    +
    574  result->free = result_free;
    +
    575 
    +
    576  return result;
    +
    577 #else
    +
    578  return NULL;
    +
    579 #endif
    +
    580 }
    +
    581 
    +
    582 /**
    +
    583  * qdb->execute_queryf(): Executes the formatted query
    +
    584  *
    +
    585  * @param db a pointer of qdb_t object
    +
    586  * @param format query string format
    +
    587  *
    +
    588  * @return a pointer of qdbresult_t if successful, otherwise returns NULL
    +
    589  */
    +
    590 static qdbresult_t *execute_queryf(qdb_t *db, const char *format, ...)
    +
    591 {
    +
    592  char *query;
    +
    593  DYNAMIC_VSPRINTF(query, format);
    +
    594  if (query == NULL) return NULL;
    +
    595 
    +
    596  qdbresult_t *ret = db->execute_query(db, query);
    +
    597  free(query);
    +
    598  return ret;
    +
    599 }
    +
    600 
    +
    601 /**
    +
    602  * qdb->begin_tran(): Start transaction
    +
    603  *
    +
    604  * @param db a pointer of qdb_t object
    +
    605  *
    +
    606  * @return true if successful, otherwise returns false
    +
    607  *
    +
    608  * @code
    +
    609  * db->begin_tran(db);
    +
    610  * (... insert/update/delete ...)
    +
    611  * db->commit(db);
    +
    612  * @endcode
    +
    613  */
    +
    614 static bool begin_tran(qdb_t *db)
    +
    615 {
    +
    616  if (db == NULL) return false;
    +
    617 
    +
    618 #ifdef Q_ENABLE_PGSQL
    +
    619  Q_MUTEX_ENTER(db->qmutex);
    +
    620  if (qtype_cast(qmutex_t*, db->qmutex)->count != 1) {
    +
    621  Q_MUTEX_LEAVE(db->qmutex);
    +
    622  return false;
    +
    623  }
    +
    624 
    +
    625  if (pgsql_query(db->pgsql, "START TRANSACTION", PQ_WRITE) < 0) {
    +
    626  Q_MUTEX_LEAVE(db->qmutex);
    +
    627  return false;
    +
    628  }
    +
    629  return true;
    +
    630 #else
    +
    631  return false;
    +
    632 #endif
    +
    633 }
    +
    634 
    +
    635 /**
    +
    636  * qdb->commit(): Commit transaction
    +
    637  *
    +
    638  * @param db a pointer of qdb_t object
    +
    639  *
    +
    640  * @return true if successful, otherwise returns false
    +
    641  */
    +
    642 static bool commit(qdb_t *db)
    +
    643 {
    +
    644  if (db == NULL) return false;
    +
    645 
    +
    646 #ifdef Q_ENABLE_PGSQL
    +
    647  bool ret = false;
    +
    648  if (pgsql_query(db->pgsql, "COMMIT", PQ_WRITE) == 0) {
    +
    649  ret = true;
    +
    650  }
    +
    651 
    +
    652  if (qtype_cast(qmutex_t*, db->qmutex)->count > 0) {
    +
    653  Q_MUTEX_LEAVE(db->qmutex);
    +
    654  }
    +
    655  return ret;
    +
    656 #else
    +
    657  return false;
    +
    658 #endif
    +
    659 }
    +
    660 
    +
    661 /**
    +
    662  * qdb->rellback(): Roll-back and abort transaction
    +
    663  *
    +
    664  * @param db a pointer of qdb_t object
    +
    665  *
    +
    666  * @return true if successful, otherwise returns false
    +
    667  */
    +
    668 static bool rollback(qdb_t *db)
    +
    669 {
    +
    670  if (db == NULL) return false;
    +
    671 
    +
    672 #ifdef Q_ENABLE_PGSQL
    +
    673  bool ret = false;
    +
    674  if (pgsql_query(db->pgsql, "ROLLBACK", PQ_WRITE) == 0) {
    +
    675  ret = true;
    +
    676  }
    +
    677 
    +
    678  if (qtype_cast(qmutex_t*, db->qmutex)->count > 0) {
    +
    679  Q_MUTEX_LEAVE(db->qmutex);
    +
    680  }
    +
    681  return ret;
    +
    682 #else
    +
    683  return 0;
    +
    684 #endif
    +
    685 }
    +
    686 
    +
    687 /**
    +
    688  * qdb->set_fetchtype(): Set result fetching type
    +
    689  *
    +
    690  * @param db a pointer of qdb_t object
    +
    691  * @param fromdb false for storing the results to client (default mode),
    +
    692  * true for fetching directly from server,
    +
    693  *
    +
    694  * @return true if successful otherwise returns false
    +
    695  *
    +
    696  * @note
    +
    697  * If qdb->set_fetchtype(db, true) is called, the results does not
    +
    698  * actually read into the client. Instead, each row must be retrieved
    +
    699  * individually by making calls to qdbresult->get_next().
    +
    700  * This reads the result of a query directly from the server without storing
    +
    701  * it in local buffer, which is somewhat faster and uses much less memory than
    +
    702  * default behavior qdb->set_fetchtype(db, false).
    +
    703  */
    +
    704 static bool set_fetchtype(qdb_t *db, bool fromdb)
    +
    705 {
    +
    706  if (db == NULL || db->pgsql == NULL) return false;
    +
    707 
    +
    708 #ifdef Q_ENABLE_PGSQL
    +
    709  pgsql_set_emsg(db->pgsql, "unsupported operation");
    +
    710 #endif
    +
    711 
    +
    712  return false;
    +
    713 }
    +
    714 
    +
    715 /**
    +
    716  * qdb->get_conn_status(): Get last connection status
    +
    717  *
    +
    718  * @param db a pointer of qdb_t object
    +
    719  *
    +
    720  * @return true if the connection flag is set to alive, otherwise returns false
    +
    721  *
    +
    722  * @note
    +
    723  * This function just returns the the connection status flag.
    +
    724  */
    +
    725 static bool get_conn_status(qdb_t *db)
    +
    726 {
    +
    727  if (db == NULL) return false;
    +
    728 
    +
    729  return db->connected;
    +
    730 }
    +
    731 
    +
    732 /**
    +
    733  * qdb->ping(): Checks whether the connection to the server is working.
    +
    734  *
    +
    735  * @param db a pointer of qdb_t object
    +
    736  *
    +
    737  * @return true if connection is alive, false if connection is not available
    +
    738  * and failed to reconnect
    +
    739  *
    +
    740  * @note
    +
    741  * If the connection has gone down, an attempt to reconnect.
    +
    742  */
    +
    743 static bool ping(qdb_t *db)
    +
    744 {
    +
    745  if (db == NULL || db->pgsql == NULL) return false;
    +
    746 
    +
    747 #ifdef Q_ENABLE_PGSQL
    +
    748  if (db->connected == true && pgsql_query(db->pgsql, NULL, PQ_PING) == 0) {
    +
    749  return true;
    +
    750  } else { // ping test failed
    +
    751  if (open_(db) == true) { // try re-connect
    +
    752  DEBUG("Connection recovered.");
    +
    753  return true;
    +
    754  }
    +
    755  }
    +
    756 
    +
    757  return false;
    +
    758 #else
    +
    759  return false;
    +
    760 #endif
    +
    761 }
    +
    762 
    +
    763 /**
    +
    764  * qdb->get_error(): Get error number and message
    +
    765  *
    +
    766  * @param db a pointer of qdb_t object
    +
    767  * @param errorno if not NULL, error number will be stored
    +
    768  *
    +
    769  * @return a pointer of error message string
    +
    770  *
    +
    771  * @note
    +
    772  * Do not free returned error message
    +
    773  */
    +
    774 static const char *get_error(qdb_t *db, unsigned int *errorno)
    +
    775 {
    +
    776  if (db == NULL || db->connected == false || db->pgsql == NULL )
    +
    777  return "(no opened db)";
    +
    778 
    +
    779  unsigned int eno;
    +
    780  const char *emsg;
    +
    781 
    +
    782 #ifdef Q_ENABLE_PGSQL
    +
    783  emsg = qtype_cast(pgsql_t*, db->pgsql)->emsg;
    +
    784  if (emsg == NULL) {
    +
    785  eno = 0;
    +
    786  emsg = "(no error)";
    +
    787  } else {
    +
    788  eno = 1;
    +
    789  }
    +
    790 #else
    +
    791  eno = 1;
    +
    792  emsg = "(not implemented)";
    +
    793 #endif
    +
    794 
    +
    795  if (errorno != NULL) *errorno = eno;
    +
    796  return emsg;
    +
    797 }
    +
    798 
    +
    799 /**
    +
    800  * qdb->free(): De-allocate qdb_t structure
    +
    801  *
    +
    802  * @param db a pointer of qdb_t object
    +
    803  */
    +
    804 static void free_(qdb_t *db)
    +
    805 {
    +
    806  if (db == NULL) return;
    +
    807 
    +
    808  Q_MUTEX_ENTER(db->qmutex);
    +
    809 
    +
    810  close_(db);
    +
    811 
    +
    812  free(db->info.dbtype);
    +
    813  free(db->info.addr);
    +
    814  free(db->info.username);
    +
    815  free(db->info.password);
    +
    816  free(db->info.database);
    +
    817  free(db);
    +
    818 
    +
    819  Q_MUTEX_LEAVE(db->qmutex);
    +
    820  Q_MUTEX_DESTROY(db->qmutex);
    +
    821 
    +
    822  return;
    +
    823 }
    +
    824 
    +
    825 /**
    +
    826  * qdbresult->get_str(): Get the result as string by field name
    +
    827  *
    +
    828  * @param result a pointer of qdbresult_t
    +
    829  * @param field column name
    +
    830  *
    +
    831  * @return a string pointer if successful, otherwise returns NULL.
    +
    832  *
    +
    833  * @note
    +
    834  * Do not free returned string.
    +
    835  */
    +
    836 static const char *_resultGetStr(qdbresult_t *result, const char *field)
    +
    837 {
    +
    838 #ifdef Q_ENABLE_PGSQL
    +
    839  if (result == NULL
    +
    840  || result->pgsql == NULL
    +
    841  || qtype_cast(pgsql_t*, result->pgsql)->cols <= 0) {
    +
    842  return NULL;
    +
    843  }
    +
    844 
    +
    845  const char *val = NULL;
    +
    846  int rows = qtype_cast(pgsql_t*, result->pgsql)->rows;
    +
    847  int cols = qtype_cast(pgsql_t*, result->pgsql)->cols;
    +
    848  int cur = qtype_cast(pgsql_t*, result->pgsql)->cursor;
    +
    849 
    +
    850  /* get row num */
    +
    851  int row = -1;
    +
    852  for (int i = 0; i < rows; i++) {
    +
    853  val = PQfname(qtype_cast(pgsql_t*, result->pgsql)->pgresult, i);
    +
    854  if (!strcasecmp(val, field)) {
    +
    855  row = i;
    +
    856  break;
    +
    857  }
    +
    858  }
    +
    859 
    +
    860  if (row == -1) {
    +
    861  return NULL;
    +
    862  }
    +
    863 
    +
    864  val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, row);
    +
    865  return val;
    +
    866 #else
    +
    867  return NULL;
    +
    868 #endif
    +
    869 }
    +
    870 
    +
    871 /**
    +
    872  * qdbresult->get_str_at(): Get the result as string by column number
    +
    873  *
    +
    874  * @param result a pointer of qdbresult_t
    +
    875  * @param idx column number (first column is 1)
    +
    876  *
    +
    877  * @return a string pointer if successful, otherwise returns NULL.
    +
    878  */
    +
    879 static const char *_resultGetStrAt(qdbresult_t *result, int idx)
    +
    880 {
    +
    881 #ifdef Q_ENABLE_PGSQL
    +
    882  if (result == NULL
    +
    883  || result->pgsql == NULL
    +
    884  || qtype_cast(pgsql_t*, result->pgsql)->rows == 0
    +
    885  || qtype_cast(pgsql_t*, result->pgsql)->cols == 0
    +
    886  || idx <= 0
    +
    887  || idx > qtype_cast(pgsql_t*, result->pgsql)->cols) {
    +
    888  return NULL;
    +
    889  }
    +
    890 
    +
    891  int cur = qtype_cast(pgsql_t*, result->pgsql)->cursor;
    +
    892  const char *val = PQgetvalue(qtype_cast(pgsql_t*, result->pgsql)->pgresult, cur, idx - 1);
    +
    893  return val;
    +
    894 #else
    +
    895  return NULL;
    +
    896 #endif
    +
    897 }
    +
    898 
    +
    899 /**
    +
    900  * qdbresult->get_int(): Get the result as integer by field name
    +
    901  *
    +
    902  * @param result a pointer of qdbresult_t
    +
    903  * @param field column name
    +
    904  *
    +
    905  * @return a integer converted value
    +
    906  */
    +
    907 static int _resultGetInt(qdbresult_t *result, const char *field)
    +
    908 {
    +
    909  const char *val = result->getstr(result, field);
    +
    910  if (val == NULL) return 0;
    +
    911  return atoi(val);
    +
    912 }
    +
    913 
    +
    914 /**
    +
    915  * qdbresult->get_int_at(): Get the result as integer by column number
    +
    916  *
    +
    917  * @param result a pointer of qdbresult_t
    +
    918  * @param idx column number (first column is 1)
    +
    919  *
    +
    920  * @return a integer converted value
    +
    921  */
    +
    922 static int _resultGetIntAt(qdbresult_t *result, int idx)
    +
    923 {
    +
    924  const char *val = result->get_str_at(result, idx);
    +
    925  if (val == NULL) return 0;
    +
    926  return atoi(val);
    +
    927 }
    +
    928 
    +
    929 /**
    +
    930  * qdbresult->get_next(): Retrieves the next row of a result set
    +
    931  *
    +
    932  * @param result a pointer of qdbresult_t
    +
    933  *
    +
    934  * @return true if successful, false if no more rows are left
    +
    935  */
    +
    936 static bool _resultGetNext(qdbresult_t *result)
    +
    937 {
    +
    938 #ifdef Q_ENABLE_PGSQL
    +
    939  if (result == NULL
    +
    940  || result->pgsql == NULL
    +
    941  || qtype_cast(pgsql_t*, result->pgsql)->pgresult == NULL) {
    +
    942  return false;
    +
    943  }
    +
    944 
    +
    945  int cursor = qtype_cast(pgsql_t*, result->pgsql)->cursor;
    +
    946  if (++cursor == qtype_cast(pgsql_t*, result->pgsql)->rows) {
    +
    947  return false;
    +
    948  }
    +
    949 
    +
    950  qtype_cast(pgsql_t*, result->pgsql)->cursor = cursor;
    +
    951  return true;
    +
    952 #else
    +
    953  return false;
    +
    954 #endif
    +
    955 }
    +
    956 
    +
    957 /**
    +
    958  * qdbresult->get_cols(): Get the number of columns in the result set
    +
    959  *
    +
    960  * @param result a pointer of qdbresult_t
    +
    961  *
    +
    962  * @return the number of columns in the result set
    +
    963  */
    +
    964 static int result_get_cols(qdbresult_t *result)
    +
    965 {
    +
    966 #ifdef Q_ENABLE_PGSQL
    +
    967  if (result == NULL || result->pgsql == NULL) return 0;
    +
    968  return qtype_cast(pgsql_t*, result->pgsql)->cols;
    +
    969 #else
    +
    970  return 0;
    +
    971 #endif
    +
    972 }
    +
    973 
    +
    974 /**
    +
    975  * qdbresult->get_rows(): Get the number of rows in the result set
    +
    976  *
    +
    977  * @param result a pointer of qdbresult_t
    +
    978  *
    +
    979  * @return the number of rows in the result set
    +
    980  */
    +
    981 static int result_get_rows(qdbresult_t *result)
    +
    982 {
    +
    983 #ifdef Q_ENABLE_PGSQL
    +
    984  if (result == NULL || result->pgsql == NULL) return 0;
    +
    985  return qtype_cast(pgsql_t*, result->pgsql)->rows;
    +
    986 #else
    +
    987  return 0;
    +
    988 #endif
    +
    989 }
    +
    990 
    +
    991 /**
    +
    992  * qdbresult->get_row(): Get the current row number
    +
    993  *
    +
    994  * @param result a pointer of qdbresult_t
    +
    995  *
    +
    996  * @return current fetching row number of the result set
    +
    997  *
    +
    998  * @note
    +
    999  * This number is sequencial counter which is started from 1.
    +
    1000  */
    +
    1001 static int result_get_row(qdbresult_t *result)
    +
    1002 {
    +
    1003 #ifdef Q_ENABLE_PGSQL
    +
    1004  if (result == NULL || result->pgsql == NULL) return 0;
    +
    1005  return qtype_cast(pgsql_t*, result->pgsql)->cursor;
    +
    1006 #else
    +
    1007  return 0;
    +
    1008 #endif
    +
    1009 }
    +
    1010 
    +
    1011 /**
    +
    1012  * qdbresult->free(): De-allocate the result
    +
    1013  *
    +
    1014  * @param result a pointer of qdbresult_t
    +
    1015  */
    +
    1016 static void result_free(qdbresult_t *result)
    +
    1017 {
    +
    1018 #ifdef Q_ENABLE_PGSQL
    +
    1019  if (result) {
    +
    1020  result->pgsql = NULL;
    +
    1021  free(result);
    +
    1022  }
    +
    1023  return;
    +
    1024 #else
    +
    1025  return;
    +
    1026 #endif
    +
    1027 }
    +
    1028 
    +
    1029 #endif
    +
    1030 
    +
    1031 #endif /* DISABLE_QDATABASE */
    +
    static bool get_conn_status(qdb_t *db)
    qdb->get_conn_status(): Get last connection status
    +
    static const char * get_error(qdb_t *db, unsigned int *errorno)
    qdb->get_error(): Get error number and message
    +
    static int result_get_rows(qdbresult_t *result)
    qdbresult->get_rows(): Get the number of rows in the result set
    +
    static bool begin_tran(qdb_t *db)
    qdb->begin_tran(): Start transaction
    +
    static bool commit(qdb_t *db)
    qdb->commit(): Commit transaction
    +
    static int result_get_row(qdbresult_t *result)
    qdbresult->get_row(): Get the current row number
    +
    static const char * _resultGetStr(qdbresult_t *result, const char *field)
    qdbresult->get_str(): Get the result as string by field name
    +
    static int execute_update(qdb_t *db, const char *query)
    qdb->execute_update(): Executes the update DML
    +
    static void result_free(qdbresult_t *result)
    qdbresult->free(): De-allocate the result
    +
    static bool set_fetchtype(qdb_t *db, bool fromdb)
    qdb->set_fetchtype(): Set result fetching type
    +
    static int _resultGetIntAt(qdbresult_t *result, int idx)
    qdbresult->get_int_at(): Get the result as integer by column number
    +
    static bool rollback(qdb_t *db)
    qdb->rellback(): Roll-back and abort transaction
    +
    static qdbresult_t * execute_queryf(qdb_t *db, const char *format,...)
    qdb->execute_queryf(): Executes the formatted query
    +
    static bool close_(qdb_t *db)
    qdb->close(): Disconnect from database server
    +
    static int result_get_cols(qdbresult_t *result)
    qdbresult->get_cols(): Get the number of columns in the result set
    +
    static bool ping(qdb_t *db)
    qdb->ping(): Checks whether the connection to the server is working.
    +
    static int execute_updatef(qdb_t *db, const char *format,...)
    qdb->execute_updatef(): Executes the formatted update DML
    +
    static int _resultGetInt(qdbresult_t *result, const char *field)
    qdbresult->get_int(): Get the result as integer by field name
    +
    static qdbresult_t * execute_query(qdb_t *db, const char *query)
    qdb->execute_query(): Executes the query
    +
    qdb_t * qdb_pgsql(const char *dbtype, const char *addr, int port, const char *database, const char *username, const char *password, bool autocommit)
    Initialize internal connector structure.
    +
    static const char * _resultGetStrAt(qdbresult_t *result, int idx)
    qdbresult->get_str_at(): Get the result as string by column number
    +
    static bool open_(qdb_t *db)
    qdb->open(): Connect to database server
    +
    static bool _resultGetNext(qdbresult_t *result)
    qdbresult->get_next(): Retrieves the next row of a result set
    +
    static void free_(qdb_t *db)
    qdb->free(): De-allocate qdb_t structure
    +
    +
    + + + + diff --git a/doc/html/qencode_8c.html b/doc/html/qencode_8c.html index ba14996b..c2347952 100644 --- a/doc/html/qencode_8c.html +++ b/doc/html/qencode_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qencode.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@

    @@ -61,7 +60,8 @@
    -
    qencode.c File Reference
    +
    +
    qencode.c File Reference
    @@ -70,28 +70,28 @@

    Go to the source code of this file.

    - - - - - - - + + + + + + - + - - - + + + - + - - - + + + - +

    +

    Functions

    qlisttbl_t * qparse_queries (qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
     Parse URL encoded query string.
     
    char * qurl_encode (const void *bin, size_t size)
     Encode data using URL encoding(Percent encoding) algorithm.
     
    qlisttbl_t * qparse_queries (qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
     Parse URL encoded query string. More...
     
    char * qurl_encode (const void *bin, size_t size)
     Encode data using URL encoding(Percent encoding) algorithm. More...
     
    size_t qurl_decode (char *str)
     Decode URL encoded string.
     Decode URL encoded string. More...
     
    char * qbase64_encode (const void *bin, size_t size)
     Encode data using BASE64 algorithm.
     
    char * qbase64_encode (const void *bin, size_t size)
     Encode data using BASE64 algorithm. More...
     
    size_t qbase64_decode (char *str)
     Decode BASE64 encoded string.
     Decode BASE64 encoded string. More...
     
    char * qhex_encode (const void *bin, size_t size)
     Encode data to Hexadecimal digit format.
     
    char * qhex_encode (const void *bin, size_t size)
     Encode data to Hexadecimal digit format. More...
     
    size_t qhex_decode (char *str)
     Decode Hexadecimal encoded data.
     Decode Hexadecimal encoded data. More...
     

    Detailed Description

    @@ -99,14 +99,14 @@

    Definition in file qencode.c.

    Function Documentation

    - -

    ◆ qparse_queries()

    + +

    ◆ qparse_queries()

    - + @@ -156,25 +156,25 @@

    Returns
    qlisttbl container pointer, otherwise returns NULL.
    cont char query = "category=love&str=%C5%A5%B5%F0%C4%DA%B4%F5&sort=asc";
    -
    qlisttbl_t *tbl = qparse_queries(NULL, req->pszQueryString, '=', '&', NULL);
    +
    qlisttbl_t *tbl = qparse_queries(NULL, req->pszQueryString, '=', '&', NULL);
    printf("category = %s\n", tbl->get_str(tbl, "category", false));
    printf("str = %s\n", tbl->get_str(tbl, "str", false));
    printf("sort = %s\n", tbl->get_str(tbl, "sort", false));
    tbl->free(tbl);
    -
    qlisttbl_t * qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
    Parse URL encoded query string.
    Definition qencode.c:62
    +
    qlisttbl_t * qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
    Parse URL encoded query string.
    Definition: qencode.c:62

    Definition at line 62 of file qencode.c.

    - -

    ◆ qurl_encode()

    + +

    ◆ qurl_encode()

    qlisttbl_t * qparse_queries qlisttbl_t* qparse_queries ( qlisttbl_t *  tbl,
    - + @@ -204,13 +204,13 @@

    Returns
    a malloced string pointer of URL encoded string in case of successful, otherwise returns NULL
    const char *text = "hello 'qLibc' world";
    -
    char *encstr = qurl_encode(text, strlen(text));
    +
    char *encstr = qurl_encode(text, strlen(text));
    if(encstr == NULL) return -1;
    printf("Original: %s\n", text);
    printf("Encoded : %s\n", encstr);
    -
    size_t decsize = qurl_decode(encstr);
    +
    size_t decsize = qurl_decode(encstr);
    printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    free(encstr);
    @@ -219,15 +219,15 @@

    Original: hello 'qLibc' world

    Encoded: hello%20%27qLibc%27%20world
    Decoded: hello 'qLibc' world (19 bytes)
    -
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition qencode.c:125
    -
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition qencode.c:192
    +
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition: qencode.c:125
    +
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition: qencode.c:192

    Definition at line 125 of file qencode.c.

    - -

    ◆ qurl_decode()

    + +

    ◆ qurl_decode()

    @@ -256,14 +256,14 @@

    -

    ◆ qbase64_encode()

    + +

    ◆ qbase64_encode()

    char * qurl_encode char* qurl_encode ( const void *  bin,
    - + @@ -293,13 +293,13 @@

    Returns
    a malloced string pointer of BASE64 encoded string in case of successful, otherwise returns NULL
    const char *text = "hello world";
    -
    char *encstr = qbase64_encode(text, strlen(text));
    +
    char *encstr = qbase64_encode(text, strlen(text));
    if(encstr == NULL) return -1;
    printf("Original: %s\n", text);
    printf("Encoded : %s\n", encstr);
    -
    size_t decsize = qbase64_decode(encstr);
    +
    size_t decsize = qbase64_decode(encstr);
    printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    free(encstr);
    @@ -308,15 +308,15 @@

    Original: hello world

    Encoded: aGVsbG8gd29ybGQ=
    Decoded: hello world (11 bytes)
    -
    size_t qbase64_decode(char *str)
    Decode BASE64 encoded string.
    Definition qencode.c:308
    -
    char * qbase64_encode(const void *bin, size_t size)
    Encode data using BASE64 algorithm.
    Definition qencode.c:249
    +
    size_t qbase64_decode(char *str)
    Decode BASE64 encoded string.
    Definition: qencode.c:308
    +
    char * qbase64_encode(const void *bin, size_t size)
    Encode data using BASE64 algorithm.
    Definition: qencode.c:249

    Definition at line 249 of file qencode.c.

    - -

    ◆ qbase64_decode()

    + +

    ◆ qbase64_decode()

    @@ -345,14 +345,14 @@

    -

    ◆ qhex_encode()

    + +

    ◆ qhex_encode()

    char * qbase64_encode char* qbase64_encode ( const void *  bin,
    - + @@ -382,13 +382,13 @@

    Returns
    a malloced string pointer of Hexadecimal encoded string in case of successful, otherwise returns NULL
    const char *text = "hello world";
    -
    char *encstr = qhex_encode(text, strlen(text));
    +
    char *encstr = qhex_encode(text, strlen(text));
    if(encstr == NULL) return -1;
    printf("Original: %s\n", text);
    printf("Encoded : %s\n", encstr);
    -
    size_t decsize = qhex_decode(encstr);
    +
    size_t decsize = qhex_decode(encstr);
    printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    free(encstr);
    @@ -399,15 +399,15 @@

    Original: hello world

    Encoded : 68656c6c6f20776f726c64
    Decoded : hello world (11 bytes)
    -
    size_t qhex_decode(char *str)
    Decode Hexadecimal encoded data.
    Definition qencode.c:426
    -
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition qencode.c:393
    +
    size_t qhex_decode(char *str)
    Decode Hexadecimal encoded data.
    Definition: qencode.c:426
    +
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition: qencode.c:393

    Definition at line 393 of file qencode.c.

    - -

    ◆ qhex_decode()

    + +

    ◆ qhex_decode()

    @@ -442,7 +442,7 @@

    diff --git a/doc/html/qencode_8c.js b/doc/html/qencode_8c.js index 85382880..ea94821b 100644 --- a/doc/html/qencode_8c.js +++ b/doc/html/qencode_8c.js @@ -1,10 +1,10 @@ var qencode_8c = [ - [ "qparse_queries", "qencode_8c.html#aaf5feec6e64a6f74b8916bde73a214f9", null ], - [ "qurl_encode", "qencode_8c.html#a711145c1cafb961bfb85d61f3e7761db", null ], + [ "qparse_queries", "qencode_8c.html#ac3a72331130b2c04d2ca3c33bf0846b3", null ], + [ "qurl_encode", "qencode_8c.html#a254eadb7e79e9c22042c0779feac9e93", null ], [ "qurl_decode", "qencode_8c.html#adefa85504919e3790adff444d8604496", null ], - [ "qbase64_encode", "qencode_8c.html#a761de56d9987f1e312e6cd0587ec104d", null ], + [ "qbase64_encode", "qencode_8c.html#ad245826b85045ed82d49ad33200ae38d", null ], [ "qbase64_decode", "qencode_8c.html#a5bb9ee36ce61ff6061c7d8a50cafdcbf", null ], - [ "qhex_encode", "qencode_8c.html#aff18a5ca486ff7af31425c4f012afd7a", null ], + [ "qhex_encode", "qencode_8c.html#acf074770f495d087f9bbb268a52debb9", null ], [ "qhex_decode", "qencode_8c.html#abe7be5bbdf9ee3928bf84f4663d272e8", null ] ]; \ No newline at end of file diff --git a/doc/html/qencode_8c_source.html b/doc/html/qencode_8c_source.html index 644f9703..aa1b14ed 100644 --- a/doc/html/qencode_8c_source.html +++ b/doc/html/qencode_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qencode.c Source File @@ -20,8 +20,8 @@

    char * qhex_encode char* qhex_encode ( const void *  bin,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,485 +52,486 @@
    -
    qencode.c
    +
    +
    qencode.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qencode.c Encoding/decoding APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <string.h>
    -
    37#include "qinternal.h"
    -
    38#include "utilities/qstring.h"
    -
    39#include "utilities/qencode.h"
    -
    40
    -
    41/**
    -
    42 * Parse URL encoded query string
    -
    43 *
    -
    44 * @param tbl a pointer of qlisttbl_t container. NULL can be used to
    -
    45 * create new table.
    -
    46 * @param query URL encoded string.
    -
    47 * @param equalchar separater of key, value pair.
    -
    48 * @param sepchar separater of line.
    -
    49 * @param count if count is not NULL, a number of parsed entries are stored.
    -
    50 *
    -
    51 * @return qlisttbl container pointer, otherwise returns NULL.
    -
    52 *
    -
    53 * @code
    -
    54 * cont char query = "category=love&str=%C5%A5%B5%F0%C4%DA%B4%F5&sort=asc";
    -
    55 * qlisttbl_t *tbl = qparse_queries(NULL, req->pszQueryString, '=', '&', NULL);
    -
    56 * printf("category = %s\n", tbl->get_str(tbl, "category", false));
    -
    57 * printf("str = %s\n", tbl->get_str(tbl, "str", false));
    -
    58 * printf("sort = %s\n", tbl->get_str(tbl, "sort", false));
    -
    59 * tbl->free(tbl);
    -
    60 * @endcode
    -
    61 */
    -
    62qlisttbl_t *qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar,
    -
    63 char sepchar, int *count) {
    -
    64 if (tbl == NULL && (tbl = qlisttbl(0)) == NULL) {
    -
    65 return NULL;
    -
    66 }
    -
    67
    -
    68 if (query == NULL) {
    -
    69 return tbl;
    -
    70 }
    -
    71
    -
    72 int cnt = 0;
    -
    73 char *newquery = strdup(query);
    -
    74 while (newquery && *newquery) {
    -
    75 char *value = _q_makeword(newquery, sepchar);
    -
    76 char *name = qstrtrim(_q_makeword(value, equalchar));
    -
    77 qurl_decode(name);
    -
    78 qurl_decode(value);
    -
    79
    -
    80 if (tbl->putstr(tbl, name, value) == true) {
    -
    81 cnt++;
    -
    82 }
    -
    83
    -
    84 free(name);
    -
    85 free(value);
    -
    86 }
    -
    87
    -
    88 if (count != NULL) {
    -
    89 *count = cnt;
    -
    90 }
    -
    91 free(newquery);
    -
    92
    -
    93 return tbl;
    -
    94}
    -
    95
    -
    96/**
    -
    97 * Encode data using URL encoding(Percent encoding) algorithm.
    -
    98 *
    -
    99 * @param bin a pointer of input data.
    -
    100 * @param size the length of input data.
    -
    101 *
    -
    102 * @return a malloced string pointer of URL encoded string in case of
    -
    103 * successful, otherwise returns NULL
    -
    104 *
    -
    105 * @code
    -
    106 * const char *text = "hello 'qLibc' world";
    -
    107 *
    -
    108 * char *encstr = qurl_encode(text, strlen(text));
    -
    109 * if(encstr == NULL) return -1;
    -
    110 *
    -
    111 * printf("Original: %s\n", text);
    -
    112 * printf("Encoded : %s\n", encstr);
    -
    113 *
    -
    114 * size_t decsize = qurl_decode(encstr);
    -
    115 *
    -
    116 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    -
    117 * free(encstr);
    -
    118 *
    -
    119 * --[output]--
    -
    120 * Original: hello 'qLibc' world
    -
    121 * Encoded: hello%20%27qLibc%27%20world
    -
    122 * Decoded: hello 'qLibc' world (19 bytes)
    -
    123 * @endcode
    -
    124 */
    -
    125char *qurl_encode(const void *bin, size_t size) {
    -
    126 const char URLCHARTBL[16*16] = {
    -
    127 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 00-0F
    -
    128 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 10-1F
    -
    129 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'-','.','/', // 20-2F
    -
    130 '0','1','2','3','4','5','6','7','8','9',':', 0 , 0 , 0 , 0 , 0 , // 30-3F
    -
    131 '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', // 40-4F
    -
    132 'P','Q','R','S','T','U','V','W','X','Y','Z', 0 ,'\\',0 , 0 ,'_', // 50-5F
    -
    133 00 ,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', // 60-6f
    -
    134 'p','q','r','s','t','u','v','w','x','y','z', 0 , 0 , 0 , 0 , 0 , // 70-7F
    -
    135 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 80-8F
    -
    136 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 90-9F
    -
    137 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // A0-AF
    -
    138 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // B0-BF
    -
    139 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // C0-CF
    -
    140 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // D0-DF
    -
    141 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // E0-EF
    -
    142 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // F0-FF
    -
    143 }; // 0 means must be encoded.
    -
    144
    -
    145 if (bin == NULL)
    -
    146 return NULL;
    -
    147 if (size == 0)
    -
    148 return strdup("");
    -
    149
    -
    150 // malloc buffer
    -
    151 char *pszEncStr = (char *) malloc((size * 3) + 1);
    -
    152 if (pszEncStr == NULL)
    -
    153 return NULL;
    -
    154
    -
    155 char *pszEncPt = pszEncStr;
    -
    156 char *pBinPt = (char *) bin;
    -
    157 const char *pBinEnd = (bin + size - 1);
    -
    158 for (; pBinPt <= pBinEnd; pBinPt++) {
    -
    159 unsigned char c = *pBinPt;
    -
    160 if (URLCHARTBL[c] != 0) {
    -
    161 *pszEncPt++ = *pBinPt;
    -
    162 } else {
    -
    163 unsigned char cUpper4 = (c >> 4);
    -
    164 unsigned char cLower4 = (c & 0x0F);
    -
    165
    -
    166 *pszEncPt++ = '%';
    -
    167 *pszEncPt++ =
    -
    168 (cUpper4 < 0x0A) ?
    -
    169 (cUpper4 + '0') : ((cUpper4 - 0x0A) + 'a');
    -
    170 *pszEncPt++ =
    -
    171 (cLower4 < 0x0A) ?
    -
    172 (cLower4 + '0') : ((cLower4 - 0x0A) + 'a');
    -
    173 }
    -
    174 }
    -
    175 *pszEncPt = '\0';
    -
    176
    -
    177 return pszEncStr;
    -
    178}
    -
    179
    -
    180/**
    -
    181 * Decode URL encoded string.
    -
    182 *
    -
    183 * @param str a pointer of URL encoded string.
    -
    184 *
    -
    185 * @return the length of bytes stored in the str memory in case of successful,
    -
    186 * otherwise returns NULL
    -
    187 *
    -
    188 * @note
    -
    189 * This modify str directly. And the 'str' is always terminated by NULL
    -
    190 * character.
    -
    191 */
    -
    192size_t qurl_decode(char *str) {
    -
    193 if (str == NULL) {
    -
    194 return 0;
    -
    195 }
    -
    196
    -
    197 char *pEncPt, *pBinPt = str;
    -
    198 for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
    -
    199 switch (*pEncPt) {
    -
    200 case '+': {
    -
    201 *pBinPt++ = ' ';
    -
    202 break;
    -
    203 }
    -
    204 case '%': {
    -
    205 *pBinPt++ = _q_x2c(*(pEncPt + 1), *(pEncPt + 2));
    -
    206 pEncPt += 2;
    -
    207 break;
    -
    208 }
    -
    209 default: {
    -
    210 *pBinPt++ = *pEncPt;
    -
    211 break;
    -
    212 }
    -
    213 }
    -
    214 }
    -
    215 *pBinPt = '\0';
    -
    216
    -
    217 return (pBinPt - str);
    -
    218}
    -
    219
    -
    220/**
    -
    221 * Encode data using BASE64 algorithm.
    -
    222 *
    -
    223 * @param bin a pointer of input data.
    -
    224 * @param size the length of input data.
    -
    225 *
    -
    226 * @return a malloced string pointer of BASE64 encoded string in case of
    -
    227 * successful, otherwise returns NULL
    -
    228 *
    -
    229 * @code
    -
    230 * const char *text = "hello world";
    -
    231 *
    -
    232 * char *encstr = qbase64_encode(text, strlen(text));
    -
    233 * if(encstr == NULL) return -1;
    -
    234 *
    -
    235 * printf("Original: %s\n", text);
    -
    236 * printf("Encoded : %s\n", encstr);
    -
    237 *
    -
    238 * size_t decsize = qbase64_decode(encstr);
    -
    239 *
    -
    240 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    -
    241 * free(encstr);
    -
    242 *
    -
    243 * --[output]--
    -
    244 * Original: hello world
    -
    245 * Encoded: aGVsbG8gd29ybGQ=
    -
    246 * Decoded: hello world (11 bytes)
    -
    247 * @endcode
    -
    248 */
    -
    249char *qbase64_encode(const void *bin, size_t size) {
    -
    250 const char B64CHARTBL[64] = {
    -
    251 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', // 00-0F
    -
    252 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', // 10-1F
    -
    253 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', // 20-2F
    -
    254 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' // 30-3F
    -
    255 };
    -
    256
    -
    257 if (size == 0) {
    -
    258 return strdup("");
    -
    259 }
    -
    260
    -
    261 // malloc for encoded string
    -
    262 char *pszB64 = (char *) malloc(
    -
    263 4 * ((size / 3) + ((size % 3 == 0) ? 0 : 1)) + 1);
    -
    264 if (pszB64 == NULL) {
    -
    265 return NULL;
    -
    266 }
    -
    267
    -
    268 char *pszB64Pt = pszB64;
    -
    269 unsigned char *pBinPt, *pBinEnd = (unsigned char *) (bin + size - 1);
    -
    270 unsigned char szIn[3] = { 0, 0, 0 };
    -
    271 int nOffset;
    -
    272 for (pBinPt = (unsigned char *) bin, nOffset = 0; pBinPt <= pBinEnd;
    -
    273 pBinPt++, nOffset++) {
    -
    274 int nIdxOfThree = nOffset % 3;
    -
    275 szIn[nIdxOfThree] = *pBinPt;
    -
    276 if (nIdxOfThree < 2 && pBinPt < pBinEnd)
    -
    277 continue;
    -
    278
    -
    279 *pszB64Pt++ = B64CHARTBL[((szIn[0] & 0xFC) >> 2)];
    -
    280 *pszB64Pt++ = B64CHARTBL[(((szIn[0] & 0x03) << 4)
    -
    281 | ((szIn[1] & 0xF0) >> 4))];
    -
    282 *pszB64Pt++ =
    -
    283 (nIdxOfThree >= 1) ?
    -
    284 B64CHARTBL[(((szIn[1] & 0x0F) << 2)
    -
    285 | ((szIn[2] & 0xC0) >> 6))] :
    -
    286 '=';
    -
    287 *pszB64Pt++ = (nIdxOfThree >= 2) ? B64CHARTBL[(szIn[2] & 0x3F)] : '=';
    -
    288
    -
    289 memset((void *) szIn, 0, sizeof(szIn));
    -
    290 }
    -
    291 *pszB64Pt = '\0';
    -
    292
    -
    293 return pszB64;
    -
    294}
    -
    295
    -
    296/**
    -
    297 * Decode BASE64 encoded string.
    -
    298 *
    -
    299 * @param str a pointer of Base64 encoded string.
    -
    300 *
    -
    301 * @return the length of bytes stored in the str memory in case of successful,
    -
    302 * otherwise returns NULL
    -
    303 *
    -
    304 * @note
    -
    305 * This modify str directly. And the 'str' is always terminated by NULL
    -
    306 * character.
    -
    307 */
    -
    308size_t qbase64_decode(char *str) {
    -
    309 const char B64MAPTBL[16 * 16] = {
    -
    310 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 00-0F
    -
    311 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 10-1F
    -
    312 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, // 20-2F
    -
    313 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, // 30-3F
    -
    314 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4F
    -
    315 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, // 50-5F
    -
    316 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6F
    -
    317 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, // 70-7F
    -
    318 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 80-8F
    -
    319 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 90-9F
    -
    320 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // A0-AF
    -
    321 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // B0-BF
    -
    322 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // C0-CF
    -
    323 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // D0-DF
    -
    324 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // E0-EF
    -
    325 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 // F0-FF
    -
    326 };
    -
    327
    -
    328 char *pEncPt, *pBinPt = str;
    -
    329 int nIdxOfFour = 0;
    -
    330 char cLastByte = 0;
    -
    331 for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
    -
    332 char cByte = B64MAPTBL[(unsigned char) (*pEncPt)];
    -
    333 if (cByte == 64)
    -
    334 continue;
    -
    335
    -
    336 if (nIdxOfFour == 0) {
    -
    337 nIdxOfFour++;
    -
    338 } else if (nIdxOfFour == 1) {
    -
    339 // 00876543 0021????
    -
    340 //*pBinPt++ = ( ((cLastByte << 2) & 0xFC) | ((cByte >> 4) & 0x03) );
    -
    341 *pBinPt++ = ((cLastByte << 2) | (cByte >> 4));
    -
    342 nIdxOfFour++;
    -
    343 } else if (nIdxOfFour == 2) {
    -
    344 // 00??8765 004321??
    -
    345 //*pBinPt++ = ( ((cLastByte << 4) & 0xF0) | ((cByte >> 2) & 0x0F) );
    -
    346 *pBinPt++ = ((cLastByte << 4) | (cByte >> 2));
    -
    347 nIdxOfFour++;
    -
    348 } else {
    -
    349 // 00????87 00654321
    -
    350 //*pBinPt++ = ( ((cLastByte << 6) & 0xC0) | (cByte & 0x3F) );
    -
    351 *pBinPt++ = ((cLastByte << 6) | cByte);
    -
    352 nIdxOfFour = 0;
    -
    353 }
    -
    354
    -
    355 cLastByte = cByte;
    -
    356 }
    -
    357 *pBinPt = '\0';
    -
    358
    -
    359 return (pBinPt - str);
    -
    360}
    -
    361
    -
    362/**
    -
    363 * Encode data to Hexadecimal digit format.
    -
    364 *
    -
    365 * @param bin a pointer of input data.
    -
    366 * @param size the length of input data.
    -
    367 *
    -
    368 * @return a malloced string pointer of Hexadecimal encoded string in case of
    -
    369 * successful, otherwise returns NULL
    -
    370 *
    -
    371 * @code
    -
    372 * const char *text = "hello world";
    -
    373 *
    -
    374 * char *encstr = qhex_encode(text, strlen(text));
    -
    375 * if(encstr == NULL) return -1;
    -
    376 *
    -
    377 * printf("Original: %s\n", text);
    -
    378 * printf("Encoded : %s\n", encstr);
    -
    379 *
    -
    380 * size_t decsize = qhex_decode(encstr);
    -
    381 *
    -
    382 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    -
    383 * free(encstr);
    -
    384 *
    -
    385 * return 0;
    -
    386 *
    -
    387 * --[output]--
    -
    388 * Original: hello world
    -
    389 * Encoded : 68656c6c6f20776f726c64
    -
    390 * Decoded : hello world (11 bytes)
    -
    391 * @endcode
    -
    392 */
    -
    393char *qhex_encode(const void *bin, size_t size) {
    -
    394 const char HEXCHARTBL[16] = {
    -
    395 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
    -
    396 };
    -
    397
    -
    398 char *pHexStr = (char *) malloc(sizeof(char) * ((size * 2) + 1));
    -
    399 if (pHexStr == NULL)
    -
    400 return NULL;
    -
    401
    -
    402 unsigned char *pSrc = (unsigned char *) bin;
    -
    403 char *pHexPt = pHexStr;
    -
    404 int i;
    -
    405 for (i = 0; i < size; i++) {
    -
    406 *pHexPt++ = HEXCHARTBL[(pSrc[i] >> 4)];
    -
    407 *pHexPt++ = HEXCHARTBL[(pSrc[i] & 0x0F)];
    -
    408 }
    -
    409 *pHexPt = '\0';
    -
    410
    -
    411 return pHexStr;
    -
    412}
    -
    413
    -
    414/**
    -
    415 * Decode Hexadecimal encoded data.
    -
    416 *
    -
    417 * @param str a pointer of Hexadecimal encoded string.
    -
    418 *
    -
    419 * @return the length of bytes stored in the str memory in case of successful,
    -
    420 * otherwise returns NULL
    -
    421 *
    -
    422 * @note
    -
    423 * This modify str directly. And the 'str' is always terminated by NULL
    -
    424 * character.
    -
    425 */
    -
    426size_t qhex_decode(char *str) {
    -
    427 const char HEXMAPTBL[16*16] = {
    -
    428 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00-0F
    -
    429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10-1F
    -
    430 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-2F
    -
    431 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 30-3F
    -
    432 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-4F
    -
    433 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-5F
    -
    434 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-6f
    -
    435 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-7F
    -
    436 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-8F
    -
    437 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-9F
    -
    438 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0-AF
    -
    439 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0-BF
    -
    440 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0-CF
    -
    441 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0-DF
    -
    442 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0-EF
    -
    443 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F0-FF
    -
    444 };
    -
    445
    -
    446 char *pEncPt, *pBinPt = str;
    -
    447 for (pEncPt = str; *pEncPt != '\0'; pEncPt += 2) {
    -
    448 *pBinPt++ = (HEXMAPTBL[(unsigned char) (*pEncPt)] << 4)
    -
    449 + HEXMAPTBL[(unsigned char) (*(pEncPt + 1))];
    -
    450 }
    -
    451 *pBinPt = '\0';
    -
    452
    -
    453 return (pBinPt - str);
    -
    454}
    -
    size_t qbase64_decode(char *str)
    Decode BASE64 encoded string.
    Definition qencode.c:308
    -
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition qencode.c:125
    -
    char * qbase64_encode(const void *bin, size_t size)
    Encode data using BASE64 algorithm.
    Definition qencode.c:249
    -
    qlisttbl_t * qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
    Parse URL encoded query string.
    Definition qencode.c:62
    -
    size_t qhex_decode(char *str)
    Decode Hexadecimal encoded data.
    Definition qencode.c:426
    -
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition qencode.c:192
    -
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition qencode.c:393
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qencode.c Encoding/decoding APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <string.h>
    +
    37 #include "qinternal.h"
    +
    38 #include "utilities/qstring.h"
    +
    39 #include "utilities/qencode.h"
    +
    40 
    +
    41 /**
    +
    42  * Parse URL encoded query string
    +
    43  *
    +
    44  * @param tbl a pointer of qlisttbl_t container. NULL can be used to
    +
    45  * create new table.
    +
    46  * @param query URL encoded string.
    +
    47  * @param equalchar separater of key, value pair.
    +
    48  * @param sepchar separater of line.
    +
    49  * @param count if count is not NULL, a number of parsed entries are stored.
    +
    50  *
    +
    51  * @return qlisttbl container pointer, otherwise returns NULL.
    +
    52  *
    +
    53  * @code
    +
    54  * cont char query = "category=love&str=%C5%A5%B5%F0%C4%DA%B4%F5&sort=asc";
    +
    55  * qlisttbl_t *tbl = qparse_queries(NULL, req->pszQueryString, '=', '&', NULL);
    +
    56  * printf("category = %s\n", tbl->get_str(tbl, "category", false));
    +
    57  * printf("str = %s\n", tbl->get_str(tbl, "str", false));
    +
    58  * printf("sort = %s\n", tbl->get_str(tbl, "sort", false));
    +
    59  * tbl->free(tbl);
    +
    60  * @endcode
    +
    61  */
    +
    62 qlisttbl_t *qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar,
    +
    63  char sepchar, int *count) {
    +
    64  if (tbl == NULL && (tbl = qlisttbl(0)) == NULL) {
    +
    65  return NULL;
    +
    66  }
    +
    67 
    +
    68  if (query == NULL) {
    +
    69  return tbl;
    +
    70  }
    +
    71 
    +
    72  int cnt = 0;
    +
    73  char *newquery = strdup(query);
    +
    74  while (newquery && *newquery) {
    +
    75  char *value = _q_makeword(newquery, sepchar);
    +
    76  char *name = qstrtrim(_q_makeword(value, equalchar));
    +
    77  qurl_decode(name);
    +
    78  qurl_decode(value);
    +
    79 
    +
    80  if (tbl->putstr(tbl, name, value) == true) {
    +
    81  cnt++;
    +
    82  }
    +
    83 
    +
    84  free(name);
    +
    85  free(value);
    +
    86  }
    +
    87 
    +
    88  if (count != NULL) {
    +
    89  *count = cnt;
    +
    90  }
    +
    91  free(newquery);
    +
    92 
    +
    93  return tbl;
    +
    94 }
    +
    95 
    +
    96 /**
    +
    97  * Encode data using URL encoding(Percent encoding) algorithm.
    +
    98  *
    +
    99  * @param bin a pointer of input data.
    +
    100  * @param size the length of input data.
    +
    101  *
    +
    102  * @return a malloced string pointer of URL encoded string in case of
    +
    103  * successful, otherwise returns NULL
    +
    104  *
    +
    105  * @code
    +
    106  * const char *text = "hello 'qLibc' world";
    +
    107  *
    +
    108  * char *encstr = qurl_encode(text, strlen(text));
    +
    109  * if(encstr == NULL) return -1;
    +
    110  *
    +
    111  * printf("Original: %s\n", text);
    +
    112  * printf("Encoded : %s\n", encstr);
    +
    113  *
    +
    114  * size_t decsize = qurl_decode(encstr);
    +
    115  *
    +
    116  * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    +
    117  * free(encstr);
    +
    118  *
    +
    119  * --[output]--
    +
    120  * Original: hello 'qLibc' world
    +
    121  * Encoded: hello%20%27qLibc%27%20world
    +
    122  * Decoded: hello 'qLibc' world (19 bytes)
    +
    123  * @endcode
    +
    124  */
    +
    125 char *qurl_encode(const void *bin, size_t size) {
    +
    126  const char URLCHARTBL[16*16] = {
    +
    127  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 00-0F
    +
    128  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 10-1F
    +
    129  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'-','.','/', // 20-2F
    +
    130  '0','1','2','3','4','5','6','7','8','9',':', 0 , 0 , 0 , 0 , 0 , // 30-3F
    +
    131  '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', // 40-4F
    +
    132  'P','Q','R','S','T','U','V','W','X','Y','Z', 0 ,'\\',0 , 0 ,'_', // 50-5F
    +
    133  00 ,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', // 60-6f
    +
    134  'p','q','r','s','t','u','v','w','x','y','z', 0 , 0 , 0 , 0 , 0 , // 70-7F
    +
    135  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 80-8F
    +
    136  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 90-9F
    +
    137  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // A0-AF
    +
    138  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // B0-BF
    +
    139  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // C0-CF
    +
    140  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // D0-DF
    +
    141  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // E0-EF
    +
    142  00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // F0-FF
    +
    143  }; // 0 means must be encoded.
    +
    144 
    +
    145  if (bin == NULL)
    +
    146  return NULL;
    +
    147  if (size == 0)
    +
    148  return strdup("");
    +
    149 
    +
    150  // malloc buffer
    +
    151  char *pszEncStr = (char *) malloc((size * 3) + 1);
    +
    152  if (pszEncStr == NULL)
    +
    153  return NULL;
    +
    154 
    +
    155  char *pszEncPt = pszEncStr;
    +
    156  char *pBinPt = (char *) bin;
    +
    157  const char *pBinEnd = (bin + size - 1);
    +
    158  for (; pBinPt <= pBinEnd; pBinPt++) {
    +
    159  unsigned char c = *pBinPt;
    +
    160  if (URLCHARTBL[c] != 0) {
    +
    161  *pszEncPt++ = *pBinPt;
    +
    162  } else {
    +
    163  unsigned char cUpper4 = (c >> 4);
    +
    164  unsigned char cLower4 = (c & 0x0F);
    +
    165 
    +
    166  *pszEncPt++ = '%';
    +
    167  *pszEncPt++ =
    +
    168  (cUpper4 < 0x0A) ?
    +
    169  (cUpper4 + '0') : ((cUpper4 - 0x0A) + 'a');
    +
    170  *pszEncPt++ =
    +
    171  (cLower4 < 0x0A) ?
    +
    172  (cLower4 + '0') : ((cLower4 - 0x0A) + 'a');
    +
    173  }
    +
    174  }
    +
    175  *pszEncPt = '\0';
    +
    176 
    +
    177  return pszEncStr;
    +
    178 }
    +
    179 
    +
    180 /**
    +
    181  * Decode URL encoded string.
    +
    182  *
    +
    183  * @param str a pointer of URL encoded string.
    +
    184  *
    +
    185  * @return the length of bytes stored in the str memory in case of successful,
    +
    186  * otherwise returns NULL
    +
    187  *
    +
    188  * @note
    +
    189  * This modify str directly. And the 'str' is always terminated by NULL
    +
    190  * character.
    +
    191  */
    +
    192 size_t qurl_decode(char *str) {
    +
    193  if (str == NULL) {
    +
    194  return 0;
    +
    195  }
    +
    196 
    +
    197  char *pEncPt, *pBinPt = str;
    +
    198  for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
    +
    199  switch (*pEncPt) {
    +
    200  case '+': {
    +
    201  *pBinPt++ = ' ';
    +
    202  break;
    +
    203  }
    +
    204  case '%': {
    +
    205  *pBinPt++ = _q_x2c(*(pEncPt + 1), *(pEncPt + 2));
    +
    206  pEncPt += 2;
    +
    207  break;
    +
    208  }
    +
    209  default: {
    +
    210  *pBinPt++ = *pEncPt;
    +
    211  break;
    +
    212  }
    +
    213  }
    +
    214  }
    +
    215  *pBinPt = '\0';
    +
    216 
    +
    217  return (pBinPt - str);
    +
    218 }
    +
    219 
    +
    220 /**
    +
    221  * Encode data using BASE64 algorithm.
    +
    222  *
    +
    223  * @param bin a pointer of input data.
    +
    224  * @param size the length of input data.
    +
    225  *
    +
    226  * @return a malloced string pointer of BASE64 encoded string in case of
    +
    227  * successful, otherwise returns NULL
    +
    228  *
    +
    229  * @code
    +
    230  * const char *text = "hello world";
    +
    231  *
    +
    232  * char *encstr = qbase64_encode(text, strlen(text));
    +
    233  * if(encstr == NULL) return -1;
    +
    234  *
    +
    235  * printf("Original: %s\n", text);
    +
    236  * printf("Encoded : %s\n", encstr);
    +
    237  *
    +
    238  * size_t decsize = qbase64_decode(encstr);
    +
    239  *
    +
    240  * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    +
    241  * free(encstr);
    +
    242  *
    +
    243  * --[output]--
    +
    244  * Original: hello world
    +
    245  * Encoded: aGVsbG8gd29ybGQ=
    +
    246  * Decoded: hello world (11 bytes)
    +
    247  * @endcode
    +
    248  */
    +
    249 char *qbase64_encode(const void *bin, size_t size) {
    +
    250  const char B64CHARTBL[64] = {
    +
    251  'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', // 00-0F
    +
    252  'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', // 10-1F
    +
    253  'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', // 20-2F
    +
    254  'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' // 30-3F
    +
    255  };
    +
    256 
    +
    257  if (size == 0) {
    +
    258  return strdup("");
    +
    259  }
    +
    260 
    +
    261  // malloc for encoded string
    +
    262  char *pszB64 = (char *) malloc(
    +
    263  4 * ((size / 3) + ((size % 3 == 0) ? 0 : 1)) + 1);
    +
    264  if (pszB64 == NULL) {
    +
    265  return NULL;
    +
    266  }
    +
    267 
    +
    268  char *pszB64Pt = pszB64;
    +
    269  unsigned char *pBinPt, *pBinEnd = (unsigned char *) (bin + size - 1);
    +
    270  unsigned char szIn[3] = { 0, 0, 0 };
    +
    271  int nOffset;
    +
    272  for (pBinPt = (unsigned char *) bin, nOffset = 0; pBinPt <= pBinEnd;
    +
    273  pBinPt++, nOffset++) {
    +
    274  int nIdxOfThree = nOffset % 3;
    +
    275  szIn[nIdxOfThree] = *pBinPt;
    +
    276  if (nIdxOfThree < 2 && pBinPt < pBinEnd)
    +
    277  continue;
    +
    278 
    +
    279  *pszB64Pt++ = B64CHARTBL[((szIn[0] & 0xFC) >> 2)];
    +
    280  *pszB64Pt++ = B64CHARTBL[(((szIn[0] & 0x03) << 4)
    +
    281  | ((szIn[1] & 0xF0) >> 4))];
    +
    282  *pszB64Pt++ =
    +
    283  (nIdxOfThree >= 1) ?
    +
    284  B64CHARTBL[(((szIn[1] & 0x0F) << 2)
    +
    285  | ((szIn[2] & 0xC0) >> 6))] :
    +
    286  '=';
    +
    287  *pszB64Pt++ = (nIdxOfThree >= 2) ? B64CHARTBL[(szIn[2] & 0x3F)] : '=';
    +
    288 
    +
    289  memset((void *) szIn, 0, sizeof(szIn));
    +
    290  }
    +
    291  *pszB64Pt = '\0';
    +
    292 
    +
    293  return pszB64;
    +
    294 }
    +
    295 
    +
    296 /**
    +
    297  * Decode BASE64 encoded string.
    +
    298  *
    +
    299  * @param str a pointer of Base64 encoded string.
    +
    300  *
    +
    301  * @return the length of bytes stored in the str memory in case of successful,
    +
    302  * otherwise returns NULL
    +
    303  *
    +
    304  * @note
    +
    305  * This modify str directly. And the 'str' is always terminated by NULL
    +
    306  * character.
    +
    307  */
    +
    308 size_t qbase64_decode(char *str) {
    +
    309  const char B64MAPTBL[16 * 16] = {
    +
    310  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 00-0F
    +
    311  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 10-1F
    +
    312  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, // 20-2F
    +
    313  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, // 30-3F
    +
    314  64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4F
    +
    315  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, // 50-5F
    +
    316  64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6F
    +
    317  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, // 70-7F
    +
    318  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 80-8F
    +
    319  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 90-9F
    +
    320  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // A0-AF
    +
    321  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // B0-BF
    +
    322  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // C0-CF
    +
    323  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // D0-DF
    +
    324  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // E0-EF
    +
    325  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 // F0-FF
    +
    326  };
    +
    327 
    +
    328  char *pEncPt, *pBinPt = str;
    +
    329  int nIdxOfFour = 0;
    +
    330  char cLastByte = 0;
    +
    331  for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
    +
    332  char cByte = B64MAPTBL[(unsigned char) (*pEncPt)];
    +
    333  if (cByte == 64)
    +
    334  continue;
    +
    335 
    +
    336  if (nIdxOfFour == 0) {
    +
    337  nIdxOfFour++;
    +
    338  } else if (nIdxOfFour == 1) {
    +
    339  // 00876543 0021????
    +
    340  //*pBinPt++ = ( ((cLastByte << 2) & 0xFC) | ((cByte >> 4) & 0x03) );
    +
    341  *pBinPt++ = ((cLastByte << 2) | (cByte >> 4));
    +
    342  nIdxOfFour++;
    +
    343  } else if (nIdxOfFour == 2) {
    +
    344  // 00??8765 004321??
    +
    345  //*pBinPt++ = ( ((cLastByte << 4) & 0xF0) | ((cByte >> 2) & 0x0F) );
    +
    346  *pBinPt++ = ((cLastByte << 4) | (cByte >> 2));
    +
    347  nIdxOfFour++;
    +
    348  } else {
    +
    349  // 00????87 00654321
    +
    350  //*pBinPt++ = ( ((cLastByte << 6) & 0xC0) | (cByte & 0x3F) );
    +
    351  *pBinPt++ = ((cLastByte << 6) | cByte);
    +
    352  nIdxOfFour = 0;
    +
    353  }
    +
    354 
    +
    355  cLastByte = cByte;
    +
    356  }
    +
    357  *pBinPt = '\0';
    +
    358 
    +
    359  return (pBinPt - str);
    +
    360 }
    +
    361 
    +
    362 /**
    +
    363  * Encode data to Hexadecimal digit format.
    +
    364  *
    +
    365  * @param bin a pointer of input data.
    +
    366  * @param size the length of input data.
    +
    367  *
    +
    368  * @return a malloced string pointer of Hexadecimal encoded string in case of
    +
    369  * successful, otherwise returns NULL
    +
    370  *
    +
    371  * @code
    +
    372  * const char *text = "hello world";
    +
    373  *
    +
    374  * char *encstr = qhex_encode(text, strlen(text));
    +
    375  * if(encstr == NULL) return -1;
    +
    376  *
    +
    377  * printf("Original: %s\n", text);
    +
    378  * printf("Encoded : %s\n", encstr);
    +
    379  *
    +
    380  * size_t decsize = qhex_decode(encstr);
    +
    381  *
    +
    382  * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
    +
    383  * free(encstr);
    +
    384  *
    +
    385  * return 0;
    +
    386  *
    +
    387  * --[output]--
    +
    388  * Original: hello world
    +
    389  * Encoded : 68656c6c6f20776f726c64
    +
    390  * Decoded : hello world (11 bytes)
    +
    391  * @endcode
    +
    392  */
    +
    393 char *qhex_encode(const void *bin, size_t size) {
    +
    394  const char HEXCHARTBL[16] = {
    +
    395  '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
    +
    396  };
    +
    397 
    +
    398  char *pHexStr = (char *) malloc(sizeof(char) * ((size * 2) + 1));
    +
    399  if (pHexStr == NULL)
    +
    400  return NULL;
    +
    401 
    +
    402  unsigned char *pSrc = (unsigned char *) bin;
    +
    403  char *pHexPt = pHexStr;
    +
    404  int i;
    +
    405  for (i = 0; i < size; i++) {
    +
    406  *pHexPt++ = HEXCHARTBL[(pSrc[i] >> 4)];
    +
    407  *pHexPt++ = HEXCHARTBL[(pSrc[i] & 0x0F)];
    +
    408  }
    +
    409  *pHexPt = '\0';
    +
    410 
    +
    411  return pHexStr;
    +
    412 }
    +
    413 
    +
    414 /**
    +
    415  * Decode Hexadecimal encoded data.
    +
    416  *
    +
    417  * @param str a pointer of Hexadecimal encoded string.
    +
    418  *
    +
    419  * @return the length of bytes stored in the str memory in case of successful,
    +
    420  * otherwise returns NULL
    +
    421  *
    +
    422  * @note
    +
    423  * This modify str directly. And the 'str' is always terminated by NULL
    +
    424  * character.
    +
    425  */
    +
    426 size_t qhex_decode(char *str) {
    +
    427  const char HEXMAPTBL[16*16] = {
    +
    428  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00-0F
    +
    429  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10-1F
    +
    430  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-2F
    +
    431  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 30-3F
    +
    432  0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-4F
    +
    433  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-5F
    +
    434  0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-6f
    +
    435  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-7F
    +
    436  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-8F
    +
    437  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-9F
    +
    438  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0-AF
    +
    439  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0-BF
    +
    440  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0-CF
    +
    441  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0-DF
    +
    442  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0-EF
    +
    443  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F0-FF
    +
    444  };
    +
    445 
    +
    446  char *pEncPt, *pBinPt = str;
    +
    447  for (pEncPt = str; *pEncPt != '\0'; pEncPt += 2) {
    +
    448  *pBinPt++ = (HEXMAPTBL[(unsigned char) (*pEncPt)] << 4)
    +
    449  + HEXMAPTBL[(unsigned char) (*(pEncPt + 1))];
    +
    450  }
    +
    451  *pBinPt = '\0';
    +
    452 
    +
    453  return (pBinPt - str);
    +
    454 }
    +
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition: qencode.c:125
    +
    size_t qbase64_decode(char *str)
    Decode BASE64 encoded string.
    Definition: qencode.c:308
    +
    size_t qhex_decode(char *str)
    Decode Hexadecimal encoded data.
    Definition: qencode.c:426
    +
    qlisttbl_t * qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
    Parse URL encoded query string.
    Definition: qencode.c:62
    +
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition: qencode.c:393
    +
    char * qbase64_encode(const void *bin, size_t size)
    Encode data using BASE64 algorithm.
    Definition: qencode.c:249
    +
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition: qencode.c:192
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    diff --git a/doc/html/qfile_8c.html b/doc/html/qfile_8c.html index b508887f..99a76d1d 100644 --- a/doc/html/qfile_8c.html +++ b/doc/html/qfile_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qfile.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qfile.c File Reference
    +
    +
    qfile.c File Reference
    @@ -71,64 +71,64 @@

    Go to the source code of this file.

    - -

    +

    Macros

    +
    #define MAX_EXTENSION_LENGTH   (8)
     
    - - + - + - + - - - - - - + + + + + + - + - + - + - - - - - - - - - + + + + + + + + + - + - - - - - - + + + + + +

    +

    Functions

    bool qfile_lock (int fd)
     Lock file.
     Lock file. More...
     
    bool qfile_unlock (int fd)
     Unlock file which is locked by qfile_lock()
     Unlock file which is locked by qfile_lock() More...
     
    bool qfile_exist (const char *filepath)
     Check file existence.
     Check file existence. More...
     
    void * qfile_load (const char *filepath, size_t *nbytes)
     Load file into memory.
     
    void * qfile_read (FILE *fp, size_t *nbytes)
     Read data from a file stream.
     
    void * qfile_load (const char *filepath, size_t *nbytes)
     Load file into memory. More...
     
    void * qfile_read (FILE *fp, size_t *nbytes)
     Read data from a file stream. More...
     
    ssize_t qfile_save (const char *filepath, const void *buf, size_t size, bool append)
     Save data into file.
     Save data into file. More...
     
    bool qfile_mkdir (const char *dirpath, mode_t mode, bool recursive)
     Attempts to create a directory recursively.
     Attempts to create a directory recursively. More...
     
    bool qfile_check_path (const char *path)
     Check path string contains invalid characters.
     Check path string contains invalid characters. More...
     
    char * qfile_get_name (const char *filepath)
     Get filename from filepath.
     
    char * qfile_get_dir (const char *filepath)
     Get directory suffix from filepath.
     
    char * qfile_get_ext (const char *filepath)
     Get extension from filepath.
     
    char * qfile_get_name (const char *filepath)
     Get filename from filepath. More...
     
    char * qfile_get_dir (const char *filepath)
     Get directory suffix from filepath. More...
     
    char * qfile_get_ext (const char *filepath)
     Get extension from filepath. More...
     
    off_t qfile_get_size (const char *filepath)
     Get file size.
     Get file size. More...
     
    char * qfile_correct_path (char *path)
     Correct path string.
     
    char * qfile_abspath (char *buf, size_t bufsize, const char *path)
     Make full absolute system path string.
     
    char * qfile_correct_path (char *path)
     Correct path string. More...
     
    char * qfile_abspath (char *buf, size_t bufsize, const char *path)
     Make full absolute system path string. More...
     

    Detailed Description

    File handling APIs.

    Definition in file qfile.c.

    Function Documentation

    - -

    ◆ qfile_lock()

    + +

    ◆ qfile_lock()

    @@ -153,27 +153,27 @@

    Returns
    true if successful, otherwise returns false.
    // for file descriptor
    int fd = open(...);
    -
    if(qfile_lock(fd) == true) {
    +
    if(qfile_lock(fd) == true) {
    (...atomic file access...)
    - +
    }
    // for FILE stream object
    FILE *fp = fopen(...);
    int fd = fileno(fp);
    -
    if(qfile_lock(fd) == true) {
    +
    if(qfile_lock(fd) == true) {
    (...atomic file access...)
    - +
    }
    -
    bool qfile_unlock(int fd)
    Unlock file which is locked by qfile_lock()
    Definition qfile.c:98
    -
    bool qfile_lock(int fd)
    Lock file.
    Definition qfile.c:74
    +
    bool qfile_unlock(int fd)
    Unlock file which is locked by qfile_lock()
    Definition: qfile.c:98
    +
    bool qfile_lock(int fd)
    Lock file.
    Definition: qfile.c:74

    Definition at line 74 of file qfile.c.

    - -

    ◆ qfile_unlock()

    + +

    ◆ qfile_unlock()

    @@ -201,8 +201,8 @@

    -

    ◆ qfile_exist()

    + +

    ◆ qfile_exist()

    @@ -230,14 +230,14 @@

    -

    ◆ qfile_load()

    + +

    ◆ qfile_load()

    - + @@ -266,30 +266,30 @@

    Returns
    allocated memory pointer if successful, otherwise returns NULL.
    // loading text file
    -
    char *text = (char *)qfile_load("/tmp/text.txt", NULL);
    +
    char *text = (char *)qfile_load("/tmp/text.txt", NULL);
    // loading binary file
    int binlen = 0;
    -
    char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    +
    char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    // loading partial
    int binlen = 10;
    -
    char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    -
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition qfile.c:159
    +
    char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    +
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition: qfile.c:159
    Note
    This method actually allocates memory more than 1 bytes than filesize then append NULL character at the end. For example, when the file size is 10 bytes long, 10+1 bytes will allocated and the last byte is always NULL character. So you can load text file and use without appending NULL character. By the way, the actual file size 10 will be returned at nbytes variable.

    Definition at line 159 of file qfile.c.

    - -

    ◆ qfile_read()

    + +

    ◆ qfile_read()

    void * qfile_load void* qfile_load ( const char *  filepath,
    - + @@ -318,16 +318,16 @@

    Returns
    allocated memory pointer if successful, otherwise returns NULL.
    int binlen = 0;
    -
    -
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition qfile.c:214
    +
    char *bin = (char *)qfile_read(fp, &binlen);
    +
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition: qfile.c:214
    Note
    This method append NULL character at the end of stream. but nbytes only counts actual readed bytes.

    Definition at line 214 of file qfile.c.

    - -

    ◆ qfile_save()

    + +

    ◆ qfile_save()

    @@ -377,19 +377,19 @@

    Returns
    the number of bytes written if successful, otherwise returns -1.
    // save text
    char *text = "hello";
    -
    qfile_save("/tmp/text.txt", (void*)text, strlen(text), false);
    +
    qfile_save("/tmp/text.txt", (void*)text, strlen(text), false);
    // save binary
    int integer1 = 75;
    -
    qfile_save("/tmp/integer.bin, (void*)&integer, sizeof(int));
    -
    ssize_t qfile_save(const char *filepath, const void *buf, size_t size, bool append)
    Save data into file.
    Definition qfile.c:283
    +
    qfile_save("/tmp/integer.bin, (void*)&integer, sizeof(int));
    +
    ssize_t qfile_save(const char *filepath, const void *buf, size_t size, bool append)
    Save data into file.
    Definition: qfile.c:283

    Definition at line 283 of file qfile.c.

    - -

    ◆ qfile_mkdir()

    + +

    ◆ qfile_mkdir()

    @@ -435,8 +435,8 @@

    -

    ◆ qfile_check_path()

    + +

    ◆ qfile_check_path()

    @@ -464,14 +464,14 @@

    -

    ◆ qfile_get_name()

    + +

    ◆ qfile_get_name()

    void * qfile_read void* qfile_read ( FILE *  fp,
    - + @@ -493,14 +493,14 @@

    -

    ◆ qfile_get_dir()

    + +

    ◆ qfile_get_dir()

    char * qfile_get_name char* qfile_get_name ( const char *  filepath)
    - + @@ -522,14 +522,14 @@

    -

    ◆ qfile_get_ext()

    + +

    ◆ qfile_get_ext()

    char * qfile_get_dir char* qfile_get_dir ( const char *  filepath)
    - + @@ -551,8 +551,8 @@

    -

    ◆ qfile_get_size()

    + +

    ◆ qfile_get_size()

    @@ -580,14 +580,14 @@

    -

    ◆ qfile_correct_path()

    + +

    ◆ qfile_correct_path()

    char * qfile_get_ext char* qfile_get_ext ( const char *  filepath)
    - + @@ -614,14 +614,14 @@

    -

    ◆ qfile_abspath()

    + +

    ◆ qfile_abspath()

    char * qfile_correct_path char* qfile_correct_path ( char *  path)
    - + @@ -658,12 +658,12 @@

    Returns
    buffer pointer if successful, otherwise returns NULL.
    char buf[PATH_MAX];
    chdir("/usr/local");
    -
    qfile_abspath(buf, sizeof(buf), "."); // returns "/usr/local"
    -
    qfile_abspath(buf, sizeof(buf), ".."); // returns "/usr"
    -
    qfile_abspath(buf, sizeof(buf), "etc"); // returns "/usr/local/etc"
    -
    qfile_abspath(buf, sizeof(buf), "/etc"); // returns "/etc"
    -
    qfile_abspath(buf, sizeof(buf), "/etc/"); // returns "/etc/"
    -
    char * qfile_abspath(char *buf, size_t bufsize, const char *path)
    Make full absolute system path string.
    Definition qfile.c:530
    +
    qfile_abspath(buf, sizeof(buf), "."); // returns "/usr/local"
    +
    qfile_abspath(buf, sizeof(buf), ".."); // returns "/usr"
    +
    qfile_abspath(buf, sizeof(buf), "etc"); // returns "/usr/local/etc"
    +
    qfile_abspath(buf, sizeof(buf), "/etc"); // returns "/etc"
    +
    qfile_abspath(buf, sizeof(buf), "/etc/"); // returns "/etc/"
    +
    char * qfile_abspath(char *buf, size_t bufsize, const char *path)
    Make full absolute system path string.
    Definition: qfile.c:530

    Definition at line 530 of file qfile.c.

    @@ -675,7 +675,7 @@

    diff --git a/doc/html/qfile_8c.js b/doc/html/qfile_8c.js index 4d083618..3d7368fa 100644 --- a/doc/html/qfile_8c.js +++ b/doc/html/qfile_8c.js @@ -1,17 +1,18 @@ var qfile_8c = [ + [ "MAX_EXTENSION_LENGTH", "qfile_8c.html#a5e813a1311d212940f55ab4686933d1f", null ], [ "qfile_lock", "qfile_8c.html#aee13ab9a468771f9b4e60ae117d1f613", null ], [ "qfile_unlock", "qfile_8c.html#a964a6571f9178ce478d61fe7c4f1021e", null ], [ "qfile_exist", "qfile_8c.html#af22eeea721ad66346d7e19f82b28fdb0", null ], - [ "qfile_load", "qfile_8c.html#a2e381f2cbdcbeeaee40dc754414cbe01", null ], - [ "qfile_read", "qfile_8c.html#a9133efad385ea27ea68c6df8fb94f1c4", null ], + [ "qfile_load", "qfile_8c.html#a0d1cd1e907efc6272ae969e702c7eb22", null ], + [ "qfile_read", "qfile_8c.html#aaa2d98ebd5690feeac3539499bf40f2e", null ], [ "qfile_save", "qfile_8c.html#aaef22ff4a8a80e6356583335cc34db64", null ], [ "qfile_mkdir", "qfile_8c.html#ae623f84683c9aa6f58ead2f16a8f63db", null ], [ "qfile_check_path", "qfile_8c.html#af382e5541d166ecb6229dcac10a029ff", null ], - [ "qfile_get_name", "qfile_8c.html#a5386ae607fac65eb68fac034a8d4e612", null ], - [ "qfile_get_dir", "qfile_8c.html#aeb6452731c1582ac80bf26f91ce494c4", null ], - [ "qfile_get_ext", "qfile_8c.html#ac4728af539fcbdc7cd00e88834cfce74", null ], + [ "qfile_get_name", "qfile_8c.html#a0321a4d7ef62cd24639626eb4ccaf751", null ], + [ "qfile_get_dir", "qfile_8c.html#aef38e80b0559f31af14dbeabbd1ac7a8", null ], + [ "qfile_get_ext", "qfile_8c.html#aed46fca5010cfdedca9da70f904210fa", null ], [ "qfile_get_size", "qfile_8c.html#af4b95ff503ddf957bc1d0d1a139a4be2", null ], - [ "qfile_correct_path", "qfile_8c.html#a3adcdafab36f7466a54538a3ae30a728", null ], - [ "qfile_abspath", "qfile_8c.html#a9b19d1e8419deecd9519759a5e37805a", null ] + [ "qfile_correct_path", "qfile_8c.html#a1f45c0b35c3a43acd632d79c6dcfea70", null ], + [ "qfile_abspath", "qfile_8c.html#a145c18b1c6a05c971129cf7d4247b921", null ] ]; \ No newline at end of file diff --git a/doc/html/qfile_8c_source.html b/doc/html/qfile_8c_source.html index c1ae6e1a..a5500b03 100644 --- a/doc/html/qfile_8c_source.html +++ b/doc/html/qfile_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qfile.c Source File @@ -20,8 +20,8 @@

    char * qfile_abspath char* qfile_abspath ( char *  buf,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,586 +52,587 @@

    -
    qfile.c
    +
    +
    qfile.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qfile.c File handling APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <string.h>
    -
    37#include <ctype.h>
    -
    38#include <unistd.h>
    -
    39#include <libgen.h>
    -
    40#include <fcntl.h>
    -
    41#include <limits.h>
    -
    42#include <errno.h>
    -
    43#include <sys/types.h>
    -
    44#include <sys/stat.h>
    -
    45#include <sys/file.h>
    -
    46#include "qinternal.h"
    -
    47#include "utilities/qstring.h"
    -
    48#include "utilities/qfile.h"
    -
    49
    -
    50/**
    -
    51 * Lock file
    -
    52 *
    -
    53 * @param fd file descriptor
    -
    54 *
    -
    55 * @return true if successful, otherwise returns false.
    -
    56 *
    -
    57 * @code
    -
    58 * // for file descriptor
    -
    59 * int fd = open(...);
    -
    60 * if(qfile_lock(fd) == true) {
    -
    61 * (...atomic file access...)
    -
    62 * qfile_unlock(fd);
    -
    63 * }
    -
    64 *
    -
    65 * // for FILE stream object
    -
    66 * FILE *fp = fopen(...);
    -
    67 * int fd = fileno(fp);
    -
    68 * if(qfile_lock(fd) == true) {
    -
    69 * (...atomic file access...)
    -
    70 * qfile_unlock(fd);
    -
    71 * }
    -
    72 * @endcode
    -
    73 */
    -
    74bool qfile_lock(int fd) {
    -
    75#ifdef _WIN32
    -
    76 return false;
    -
    77#else
    -
    78 struct flock lock;
    -
    79 memset((void *) &lock, 0, sizeof(flock));
    -
    80 lock.l_type = F_WRLCK;
    -
    81 lock.l_whence = SEEK_SET;
    -
    82 lock.l_start = 0;
    -
    83 lock.l_len = 0;
    -
    84 int ret = fcntl(fd, F_SETLK, &lock);
    -
    85 if (ret == 0)
    -
    86 return true;
    -
    87 return false;
    -
    88#endif
    -
    89}
    -
    90
    -
    91/**
    -
    92 * Unlock file which is locked by qfile_lock()
    -
    93 *
    -
    94 * @param fd file descriptor
    -
    95 *
    -
    96 * @return true if successful, otherwise returns false.
    -
    97 */
    -
    98bool qfile_unlock(int fd) {
    -
    99#ifdef _WIN32
    -
    100 return false;
    -
    101#else
    -
    102 struct flock lock;
    -
    103 memset((void *) &lock, 0, sizeof(flock));
    -
    104 lock.l_type = F_UNLCK;
    -
    105 lock.l_whence = SEEK_SET;
    -
    106 lock.l_start = 0;
    -
    107 lock.l_len = 0;
    -
    108 int ret = fcntl(fd, F_SETLK, &lock);
    -
    109 if (ret == 0)
    -
    110 return true;
    -
    111 return false;
    -
    112#endif
    -
    113}
    -
    114
    -
    115/**
    -
    116 * Check file existence.
    -
    117 *
    -
    118 * @param filepath file or directory path
    -
    119 *
    -
    120 * @return true if exists, otherwise returns false.
    -
    121 */
    -
    122bool qfile_exist(const char *filepath) {
    -
    123 if (access(filepath, F_OK) == 0)
    -
    124 return true;
    -
    125 return false;
    -
    126}
    -
    127
    -
    128/**
    -
    129 * Load file into memory.
    -
    130 *
    -
    131 * @param filepath file path
    -
    132 * @param nbytes has two purpost, one is to set how many bytes are readed.
    -
    133 * the other is actual the number loaded bytes will be stored.
    -
    134 * nbytes must be point 0 or NULL to read entire file.
    -
    135 *
    -
    136 * @return allocated memory pointer if successful, otherwise returns NULL.
    -
    137 *
    -
    138 * @code
    -
    139 * // loading text file
    -
    140 * char *text = (char *)qfile_load("/tmp/text.txt", NULL);
    -
    141 *
    -
    142 * // loading binary file
    -
    143 * int binlen = 0;
    -
    144 * char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    -
    145 *
    -
    146 * // loading partial
    -
    147 * int binlen = 10;
    -
    148 * char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    -
    149 * @endcode
    -
    150 *
    -
    151 * @note
    -
    152 * This method actually allocates memory more than 1 bytes than filesize then
    -
    153 * append NULL character at the end. For example, when the file size is 10
    -
    154 * bytes long, 10+1 bytes will allocated and the last byte is always NULL
    -
    155 * character. So you can load text file and use without appending NULL
    -
    156 * character. By the way, the actual file size 10 will be returned at nbytes
    -
    157 * variable.
    -
    158 */
    -
    159void *qfile_load(const char *filepath, size_t *nbytes) {
    -
    160 int fd;
    -
    161 if ((fd = open(filepath, O_RDONLY, 0)) < 0)
    -
    162 return NULL;
    -
    163
    -
    164 struct stat fs;
    -
    165 if (fstat(fd, &fs) < 0) {
    -
    166 close(fd);
    -
    167 return NULL;
    -
    168 }
    -
    169
    -
    170 size_t size = fs.st_size;
    -
    171 if (nbytes != NULL && *nbytes > 0 && *nbytes < fs.st_size)
    -
    172 size = *nbytes;
    -
    173
    -
    174 void *buf = malloc(size + 1);
    -
    175 if (buf == NULL) {
    -
    176 close(fd);
    -
    177 return NULL;
    -
    178 }
    -
    179
    -
    180 ssize_t count = read(fd, buf, size);
    -
    181 close(fd);
    -
    182
    -
    183 if (count != size) {
    -
    184 free(buf);
    -
    185 return NULL;
    -
    186 }
    -
    187
    -
    188 ((char *) buf)[count] = '\0';
    -
    189
    -
    190 if (nbytes != NULL)
    -
    191 *nbytes = count;
    -
    192 return buf;
    -
    193}
    -
    194
    -
    195/**
    -
    196 * Read data from a file stream.
    -
    197 *
    -
    198 * @param fp FILE pointer
    -
    199 * @param nbytes has two purpose, one is to set bytes to read.
    -
    200 * the other is to return actual number of bytes loaded.
    -
    201 * 0 or NULL can be set to read file until the end.
    -
    202 *
    -
    203 * @return allocated memory pointer if successful, otherwise returns NULL.
    -
    204 *
    -
    205 * @code
    -
    206 * int binlen = 0;
    -
    207 * char *bin = (char *)qfile_read(fp, &binlen);
    -
    208 * @endcode
    -
    209 *
    -
    210 * @note
    -
    211 * This method append NULL character at the end of stream. but nbytes only
    -
    212 * counts actual readed bytes.
    -
    213 */
    -
    214void *qfile_read(FILE *fp, size_t *nbytes) {
    -
    215 size_t memsize = 1024;
    -
    216 size_t size = 0;
    -
    217
    -
    218 if (nbytes != NULL && *nbytes > 0) {
    -
    219 memsize = *nbytes;
    -
    220 size = *nbytes;
    -
    221 }
    -
    222
    -
    223 int c;
    -
    224 size_t c_count;
    -
    225 char *data = NULL;
    -
    226 for (c_count = 0; (c = fgetc(fp)) != EOF;) {
    -
    227 if (size > 0 && c_count == size)
    -
    228 break;
    -
    229
    -
    230 if (c_count == 0) {
    -
    231 data = (char *) malloc(sizeof(char) * memsize);
    -
    232 if (data == NULL) {
    -
    233 DEBUG("Memory allocation failed.");
    -
    234 return NULL;
    -
    235 }
    -
    236 } else if (c_count == memsize - 1) {
    -
    237 memsize *= 2;
    -
    238
    -
    239 /* Here, we don't use realloc(). Sometimes it is unstable. */
    -
    240 char *datatmp = (char *) malloc(sizeof(char) * (memsize + 1));
    -
    241 if (datatmp == NULL) {
    -
    242 DEBUG("Memory allocation failed.");
    -
    243 free(data);
    -
    244 return NULL;
    -
    245 }
    -
    246 memcpy(datatmp, data, c_count);
    -
    247 free(data);
    -
    248 data = datatmp;
    -
    249 }
    -
    250 data[c_count++] = (char) c;
    -
    251 }
    -
    252
    -
    253 if (c_count == 0 && c == EOF)
    -
    254 return NULL;
    -
    255 data[c_count] = '\0';
    -
    256
    -
    257 if (nbytes != NULL)
    -
    258 *nbytes = c_count;
    -
    259
    -
    260 return (void *) data;
    -
    261}
    -
    262
    -
    263/**
    -
    264 * Save data into file.
    -
    265 *
    -
    266 * @param filepath file path
    -
    267 * @param buf data
    -
    268 * @param size the number of bytes to save
    -
    269 * @param append false for new(if exists truncate), true for appending
    -
    270 *
    -
    271 * @return the number of bytes written if successful, otherwise returns -1.
    -
    272 *
    -
    273 * @code
    -
    274 * // save text
    -
    275 * char *text = "hello";
    -
    276 * qfile_save("/tmp/text.txt", (void*)text, strlen(text), false);
    -
    277 *
    -
    278 * // save binary
    -
    279 * int integer1 = 75;
    -
    280 * qfile_save("/tmp/integer.bin, (void*)&integer, sizeof(int));
    -
    281 * @endcode
    -
    282 */
    -
    283ssize_t qfile_save(const char *filepath, const void *buf, size_t size,
    -
    284 bool append) {
    -
    285 int fd;
    -
    286
    -
    287 if (append == false) {
    -
    288 fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC,
    -
    289 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    -
    290 } else {
    -
    291 fd = open(filepath, O_CREAT | O_WRONLY | O_APPEND,
    -
    292 (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    -
    293 }
    -
    294 if (fd < 0)
    -
    295 return -1;
    -
    296
    -
    297 ssize_t count = write(fd, buf, size);
    -
    298 close(fd);
    -
    299
    -
    300 return count;
    -
    301}
    -
    302
    -
    303/**
    -
    304 * Attempts to create a directory recursively.
    -
    305 *
    -
    306 * @param dirpath directory path
    -
    307 * @param mode permissions to use
    -
    308 * @param recursive whether or not to create parent directories automatically
    -
    309 *
    -
    310 * @return true if successful, otherwise returns false.
    -
    311 */
    -
    312bool qfile_mkdir(const char *dirpath, mode_t mode, bool recursive) {
    -
    313 DEBUG("try to create directory %s", dirpath);
    -
    314 if (mkdir(dirpath, mode) == 0)
    -
    315 return true;
    -
    316
    -
    317 bool ret = false;
    -
    318 if (recursive == true && errno == ENOENT) {
    -
    319 char *parentpath = qfile_get_dir(dirpath);
    -
    320 if (qfile_mkdir(parentpath, mode, true) == true
    -
    321 && mkdir(dirpath, mode) == 0) {
    -
    322 ret = true;
    -
    323 }
    -
    324 free(parentpath);
    -
    325 }
    -
    326
    -
    327 return ret;
    -
    328}
    -
    329
    -
    330/**
    -
    331 * Check path string contains invalid characters.
    -
    332 *
    -
    333 * @param path path string
    -
    334 *
    -
    335 * @return true if ok, otherwise returns false.
    -
    336 */
    -
    337bool qfile_check_path(const char *path) {
    -
    338 if (path == NULL)
    -
    339 return false;
    -
    340
    -
    341 int nLen = strlen(path);
    -
    342 if (nLen == 0 || nLen >= PATH_MAX)
    -
    343 return false;
    -
    344 else if (strpbrk(path, "\\:*?\"<>|") != NULL)
    -
    345 return false;
    -
    346 return true;
    -
    347}
    -
    348
    -
    349/**
    -
    350 * Get filename from filepath
    -
    351 *
    -
    352 * @param filepath file or directory path
    -
    353 *
    -
    354 * @return malloced filename string
    -
    355 */
    -
    356char *qfile_get_name(const char *filepath) {
    -
    357 char *path = strdup(filepath);
    -
    358 char *bname = basename(path);
    -
    359 char *filename = strdup(bname);
    -
    360 free(path);
    -
    361 return filename;
    -
    362}
    -
    363
    -
    364/**
    -
    365 * Get directory suffix from filepath
    -
    366 *
    -
    367 * @param filepath file or directory path
    -
    368 *
    -
    369 * @return malloced filepath string
    -
    370 */
    -
    371char *qfile_get_dir(const char *filepath) {
    -
    372 char *path = strdup(filepath);
    -
    373 char *dname = dirname(path);
    -
    374 char *dir = strdup(dname);
    -
    375 free(path);
    -
    376 return dir;
    -
    377}
    -
    378
    -
    379/**
    -
    380 * Get extension from filepath.
    -
    381 *
    -
    382 * @param filepath file or directory path
    -
    383 *
    -
    384 * @return malloced extension string which is converted to lower case.
    -
    385 */
    -
    386char *qfile_get_ext(const char *filepath) {
    -
    387#define MAX_EXTENSION_LENGTH (8)
    -
    388 char *filename = qfile_get_name(filepath);
    -
    389 char *p = strrchr(filename, '.');
    -
    390 char *ext = NULL;
    -
    391 if (p != NULL && strlen(p + 1) <= MAX_EXTENSION_LENGTH
    -
    392 && qstrtest(isalnum, p + 1) == true) {
    -
    393 ext = strdup(p + 1);
    -
    394 qstrlower(ext);
    -
    395 } else {
    -
    396 ext = strdup("");
    -
    397 }
    -
    398
    -
    399 free(filename);
    -
    400 return ext;
    -
    401}
    -
    402
    -
    403/**
    -
    404 * Get file size.
    -
    405 *
    -
    406 * @param filepath file or directory path
    -
    407 *
    -
    408 * @return the file size if exists, otherwise returns -1.
    -
    409 */
    -
    410off_t qfile_get_size(const char *filepath) {
    -
    411 struct stat finfo;
    -
    412 if (stat(filepath, &finfo) < 0)
    -
    413 return -1;
    -
    414 return finfo.st_size;
    -
    415}
    -
    416
    -
    417/**
    -
    418 * Correct path string.
    -
    419 *
    -
    420 * @param path path string
    -
    421 *
    -
    422 * @return path string pointer
    -
    423 *
    -
    424 * @note
    -
    425 * This modify path argument itself.
    -
    426 *
    -
    427 * @code
    -
    428 * "/hello//my/../world" => "/hello/world"
    -
    429 * "././././hello/./world" => "./hello/world"
    -
    430 * "/../hello//world" => "/hello/world"
    -
    431 * "/../hello//world/" => "/hello/world"
    -
    432 * @endcode
    -
    433 */
    -
    434char *qfile_correct_path(char *path) {
    -
    435 if (path == NULL)
    -
    436 return NULL;
    -
    437
    -
    438 // take off heading & tailing white spaces
    -
    439 qstrtrim(path);
    -
    440
    -
    441 while (true) {
    -
    442 // take off double slashes
    -
    443 if (strstr(path, "//") != NULL) {
    -
    444 qstrreplace("sr", path, "//", "/");
    -
    445 continue;
    -
    446 }
    -
    447
    -
    448 if (strstr(path, "/./") != NULL) {
    -
    449 qstrreplace("sr", path, "/./", "/");
    -
    450 continue;
    -
    451 }
    -
    452
    -
    453 if (strstr(path, "/../") != NULL) {
    -
    454 char *pszTmp = strstr(path, "/../");
    -
    455 if (pszTmp == path) {
    -
    456 // /../hello => /hello
    -
    457 strcpy(path, pszTmp + 3);
    -
    458 } else {
    -
    459 // /hello/../world => /world
    -
    460 *pszTmp = '\0';
    -
    461 char *pszNewPrefix = qfile_get_dir(path);
    -
    462 strcpy(path, pszNewPrefix);
    -
    463 strcat(path, pszTmp + 3);
    -
    464 free(pszNewPrefix);
    -
    465 }
    -
    466 continue;
    -
    467 }
    -
    468
    -
    469 // take off tailing slash
    -
    470 size_t nLen = strlen(path);
    -
    471 if (nLen > 1) {
    -
    472 if (path[nLen - 1] == '/') {
    -
    473 path[nLen - 1] = '\0';
    -
    474 continue;
    -
    475 }
    -
    476 }
    -
    477
    -
    478 // take off tailing /.
    -
    479 if (nLen > 2) {
    -
    480 if (!strcmp(path + (nLen - 2), "/.")) {
    -
    481 path[nLen - 2] = '\0';
    -
    482 continue;
    -
    483 }
    -
    484 }
    -
    485
    -
    486 // take off tailing /.
    -
    487 if (nLen > 2) {
    -
    488 if (!strcmp(path + (nLen - 2), "/.")) {
    -
    489 path[nLen - 2] = '\0';
    -
    490 continue;
    -
    491 }
    -
    492 }
    -
    493
    -
    494 // take off tailing /.
    -
    495 if (nLen > 3) {
    -
    496 if (!strcmp(path + (nLen - 3), "/..")) {
    -
    497 path[nLen - 3] = '\0';
    -
    498 char *pszNewPath = qfile_get_dir(path);
    -
    499 strcpy(path, pszNewPath);
    -
    500 free(pszNewPath);
    -
    501 continue;
    -
    502 }
    -
    503 }
    -
    504
    -
    505 break;
    -
    506 }
    -
    507
    -
    508 return path;
    -
    509}
    -
    510
    -
    511/**
    -
    512 * Make full absolute system path string.
    -
    513 *
    -
    514 * @param buf buffer string pointer
    -
    515 * @param bufsize buffer size
    -
    516 * @param path path string
    -
    517 *
    -
    518 * @return buffer pointer if successful, otherwise returns NULL.
    -
    519 *
    -
    520 * @code
    -
    521 * char buf[PATH_MAX];
    -
    522 * chdir("/usr/local");
    -
    523 * qfile_abspath(buf, sizeof(buf), "."); // returns "/usr/local"
    -
    524 * qfile_abspath(buf, sizeof(buf), ".."); // returns "/usr"
    -
    525 * qfile_abspath(buf, sizeof(buf), "etc"); // returns "/usr/local/etc"
    -
    526 * qfile_abspath(buf, sizeof(buf), "/etc"); // returns "/etc"
    -
    527 * qfile_abspath(buf, sizeof(buf), "/etc/"); // returns "/etc/"
    -
    528 * @endcode
    -
    529 */
    -
    530char *qfile_abspath(char *buf, size_t bufsize, const char *path) {
    -
    531 if (bufsize == 0)
    -
    532 return NULL;
    -
    533
    -
    534 if (path[0] == '/') {
    -
    535 qstrcpy(buf, bufsize, path);
    -
    536 } else {
    -
    537 if (getcwd(buf, bufsize) == NULL)
    -
    538 return NULL;
    -
    539 strcat(buf, "/");
    -
    540 strcat(buf, path);
    -
    541 }
    - -
    543
    -
    544 return buf;
    -
    545}
    -
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition qfile.c:159
    -
    char * qfile_correct_path(char *path)
    Correct path string.
    Definition qfile.c:434
    -
    char * qfile_get_name(const char *filepath)
    Get filename from filepath.
    Definition qfile.c:356
    -
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition qfile.c:214
    -
    bool qfile_unlock(int fd)
    Unlock file which is locked by qfile_lock()
    Definition qfile.c:98
    -
    char * qfile_abspath(char *buf, size_t bufsize, const char *path)
    Make full absolute system path string.
    Definition qfile.c:530
    -
    ssize_t qfile_save(const char *filepath, const void *buf, size_t size, bool append)
    Save data into file.
    Definition qfile.c:283
    -
    char * qfile_get_ext(const char *filepath)
    Get extension from filepath.
    Definition qfile.c:386
    -
    bool qfile_mkdir(const char *dirpath, mode_t mode, bool recursive)
    Attempts to create a directory recursively.
    Definition qfile.c:312
    -
    char * qfile_get_dir(const char *filepath)
    Get directory suffix from filepath.
    Definition qfile.c:371
    -
    bool qfile_lock(int fd)
    Lock file.
    Definition qfile.c:74
    -
    bool qfile_exist(const char *filepath)
    Check file existence.
    Definition qfile.c:122
    -
    bool qfile_check_path(const char *path)
    Check path string contains invalid characters.
    Definition qfile.c:337
    -
    off_t qfile_get_size(const char *filepath)
    Get file size.
    Definition qfile.c:410
    -
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition qstring.c:325
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    -
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition qstring.c:752
    -
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition qstring.c:236
    -
    char * qstrlower(char *str)
    Convert character to lower character.
    Definition qstring.c:543
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qfile.c File handling APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <string.h>
    +
    37 #include <ctype.h>
    +
    38 #include <unistd.h>
    +
    39 #include <libgen.h>
    +
    40 #include <fcntl.h>
    +
    41 #include <limits.h>
    +
    42 #include <errno.h>
    +
    43 #include <sys/types.h>
    +
    44 #include <sys/stat.h>
    +
    45 #include <sys/file.h>
    +
    46 #include "qinternal.h"
    +
    47 #include "utilities/qstring.h"
    +
    48 #include "utilities/qfile.h"
    +
    49 
    +
    50 /**
    +
    51  * Lock file
    +
    52  *
    +
    53  * @param fd file descriptor
    +
    54  *
    +
    55  * @return true if successful, otherwise returns false.
    +
    56  *
    +
    57  * @code
    +
    58  * // for file descriptor
    +
    59  * int fd = open(...);
    +
    60  * if(qfile_lock(fd) == true) {
    +
    61  * (...atomic file access...)
    +
    62  * qfile_unlock(fd);
    +
    63  * }
    +
    64  *
    +
    65  * // for FILE stream object
    +
    66  * FILE *fp = fopen(...);
    +
    67  * int fd = fileno(fp);
    +
    68  * if(qfile_lock(fd) == true) {
    +
    69  * (...atomic file access...)
    +
    70  * qfile_unlock(fd);
    +
    71  * }
    +
    72  * @endcode
    +
    73  */
    +
    74 bool qfile_lock(int fd) {
    +
    75 #ifdef _WIN32
    +
    76  return false;
    +
    77 #else
    +
    78  struct flock lock;
    +
    79  memset((void *) &lock, 0, sizeof(flock));
    +
    80  lock.l_type = F_WRLCK;
    +
    81  lock.l_whence = SEEK_SET;
    +
    82  lock.l_start = 0;
    +
    83  lock.l_len = 0;
    +
    84  int ret = fcntl(fd, F_SETLK, &lock);
    +
    85  if (ret == 0)
    +
    86  return true;
    +
    87  return false;
    +
    88 #endif
    +
    89 }
    +
    90 
    +
    91 /**
    +
    92  * Unlock file which is locked by qfile_lock()
    +
    93  *
    +
    94  * @param fd file descriptor
    +
    95  *
    +
    96  * @return true if successful, otherwise returns false.
    +
    97  */
    +
    98 bool qfile_unlock(int fd) {
    +
    99 #ifdef _WIN32
    +
    100  return false;
    +
    101 #else
    +
    102  struct flock lock;
    +
    103  memset((void *) &lock, 0, sizeof(flock));
    +
    104  lock.l_type = F_UNLCK;
    +
    105  lock.l_whence = SEEK_SET;
    +
    106  lock.l_start = 0;
    +
    107  lock.l_len = 0;
    +
    108  int ret = fcntl(fd, F_SETLK, &lock);
    +
    109  if (ret == 0)
    +
    110  return true;
    +
    111  return false;
    +
    112 #endif
    +
    113 }
    +
    114 
    +
    115 /**
    +
    116  * Check file existence.
    +
    117  *
    +
    118  * @param filepath file or directory path
    +
    119  *
    +
    120  * @return true if exists, otherwise returns false.
    +
    121  */
    +
    122 bool qfile_exist(const char *filepath) {
    +
    123  if (access(filepath, F_OK) == 0)
    +
    124  return true;
    +
    125  return false;
    +
    126 }
    +
    127 
    +
    128 /**
    +
    129  * Load file into memory.
    +
    130  *
    +
    131  * @param filepath file path
    +
    132  * @param nbytes has two purpost, one is to set how many bytes are readed.
    +
    133  * the other is actual the number loaded bytes will be stored.
    +
    134  * nbytes must be point 0 or NULL to read entire file.
    +
    135  *
    +
    136  * @return allocated memory pointer if successful, otherwise returns NULL.
    +
    137  *
    +
    138  * @code
    +
    139  * // loading text file
    +
    140  * char *text = (char *)qfile_load("/tmp/text.txt", NULL);
    +
    141  *
    +
    142  * // loading binary file
    +
    143  * int binlen = 0;
    +
    144  * char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    +
    145  *
    +
    146  * // loading partial
    +
    147  * int binlen = 10;
    +
    148  * char *bin = (char *)qfile_load("/tmp/binary.bin", &binlen);
    +
    149  * @endcode
    +
    150  *
    +
    151  * @note
    +
    152  * This method actually allocates memory more than 1 bytes than filesize then
    +
    153  * append NULL character at the end. For example, when the file size is 10
    +
    154  * bytes long, 10+1 bytes will allocated and the last byte is always NULL
    +
    155  * character. So you can load text file and use without appending NULL
    +
    156  * character. By the way, the actual file size 10 will be returned at nbytes
    +
    157  * variable.
    +
    158  */
    +
    159 void *qfile_load(const char *filepath, size_t *nbytes) {
    +
    160  int fd;
    +
    161  if ((fd = open(filepath, O_RDONLY, 0)) < 0)
    +
    162  return NULL;
    +
    163 
    +
    164  struct stat fs;
    +
    165  if (fstat(fd, &fs) < 0) {
    +
    166  close(fd);
    +
    167  return NULL;
    +
    168  }
    +
    169 
    +
    170  size_t size = fs.st_size;
    +
    171  if (nbytes != NULL && *nbytes > 0 && *nbytes < fs.st_size)
    +
    172  size = *nbytes;
    +
    173 
    +
    174  void *buf = malloc(size + 1);
    +
    175  if (buf == NULL) {
    +
    176  close(fd);
    +
    177  return NULL;
    +
    178  }
    +
    179 
    +
    180  ssize_t count = read(fd, buf, size);
    +
    181  close(fd);
    +
    182 
    +
    183  if (count != size) {
    +
    184  free(buf);
    +
    185  return NULL;
    +
    186  }
    +
    187 
    +
    188  ((char *) buf)[count] = '\0';
    +
    189 
    +
    190  if (nbytes != NULL)
    +
    191  *nbytes = count;
    +
    192  return buf;
    +
    193 }
    +
    194 
    +
    195 /**
    +
    196  * Read data from a file stream.
    +
    197  *
    +
    198  * @param fp FILE pointer
    +
    199  * @param nbytes has two purpose, one is to set bytes to read.
    +
    200  * the other is to return actual number of bytes loaded.
    +
    201  * 0 or NULL can be set to read file until the end.
    +
    202  *
    +
    203  * @return allocated memory pointer if successful, otherwise returns NULL.
    +
    204  *
    +
    205  * @code
    +
    206  * int binlen = 0;
    +
    207  * char *bin = (char *)qfile_read(fp, &binlen);
    +
    208  * @endcode
    +
    209  *
    +
    210  * @note
    +
    211  * This method append NULL character at the end of stream. but nbytes only
    +
    212  * counts actual readed bytes.
    +
    213  */
    +
    214 void *qfile_read(FILE *fp, size_t *nbytes) {
    +
    215  size_t memsize = 1024;
    +
    216  size_t size = 0;
    +
    217 
    +
    218  if (nbytes != NULL && *nbytes > 0) {
    +
    219  memsize = *nbytes;
    +
    220  size = *nbytes;
    +
    221  }
    +
    222 
    +
    223  int c;
    +
    224  size_t c_count;
    +
    225  char *data = NULL;
    +
    226  for (c_count = 0; (c = fgetc(fp)) != EOF;) {
    +
    227  if (size > 0 && c_count == size)
    +
    228  break;
    +
    229 
    +
    230  if (c_count == 0) {
    +
    231  data = (char *) malloc(sizeof(char) * memsize);
    +
    232  if (data == NULL) {
    +
    233  DEBUG("Memory allocation failed.");
    +
    234  return NULL;
    +
    235  }
    +
    236  } else if (c_count == memsize - 1) {
    +
    237  memsize *= 2;
    +
    238 
    +
    239  /* Here, we don't use realloc(). Sometimes it is unstable. */
    +
    240  char *datatmp = (char *) malloc(sizeof(char) * (memsize + 1));
    +
    241  if (datatmp == NULL) {
    +
    242  DEBUG("Memory allocation failed.");
    +
    243  free(data);
    +
    244  return NULL;
    +
    245  }
    +
    246  memcpy(datatmp, data, c_count);
    +
    247  free(data);
    +
    248  data = datatmp;
    +
    249  }
    +
    250  data[c_count++] = (char) c;
    +
    251  }
    +
    252 
    +
    253  if (c_count == 0 && c == EOF)
    +
    254  return NULL;
    +
    255  data[c_count] = '\0';
    +
    256 
    +
    257  if (nbytes != NULL)
    +
    258  *nbytes = c_count;
    +
    259 
    +
    260  return (void *) data;
    +
    261 }
    +
    262 
    +
    263 /**
    +
    264  * Save data into file.
    +
    265  *
    +
    266  * @param filepath file path
    +
    267  * @param buf data
    +
    268  * @param size the number of bytes to save
    +
    269  * @param append false for new(if exists truncate), true for appending
    +
    270  *
    +
    271  * @return the number of bytes written if successful, otherwise returns -1.
    +
    272  *
    +
    273  * @code
    +
    274  * // save text
    +
    275  * char *text = "hello";
    +
    276  * qfile_save("/tmp/text.txt", (void*)text, strlen(text), false);
    +
    277  *
    +
    278  * // save binary
    +
    279  * int integer1 = 75;
    +
    280  * qfile_save("/tmp/integer.bin, (void*)&integer, sizeof(int));
    +
    281  * @endcode
    +
    282  */
    +
    283 ssize_t qfile_save(const char *filepath, const void *buf, size_t size,
    +
    284  bool append) {
    +
    285  int fd;
    +
    286 
    +
    287  if (append == false) {
    +
    288  fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC,
    +
    289  (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    +
    290  } else {
    +
    291  fd = open(filepath, O_CREAT | O_WRONLY | O_APPEND,
    +
    292  (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
    +
    293  }
    +
    294  if (fd < 0)
    +
    295  return -1;
    +
    296 
    +
    297  ssize_t count = write(fd, buf, size);
    +
    298  close(fd);
    +
    299 
    +
    300  return count;
    +
    301 }
    +
    302 
    +
    303 /**
    +
    304  * Attempts to create a directory recursively.
    +
    305  *
    +
    306  * @param dirpath directory path
    +
    307  * @param mode permissions to use
    +
    308  * @param recursive whether or not to create parent directories automatically
    +
    309  *
    +
    310  * @return true if successful, otherwise returns false.
    +
    311  */
    +
    312 bool qfile_mkdir(const char *dirpath, mode_t mode, bool recursive) {
    +
    313  DEBUG("try to create directory %s", dirpath);
    +
    314  if (mkdir(dirpath, mode) == 0)
    +
    315  return true;
    +
    316 
    +
    317  bool ret = false;
    +
    318  if (recursive == true && errno == ENOENT) {
    +
    319  char *parentpath = qfile_get_dir(dirpath);
    +
    320  if (qfile_mkdir(parentpath, mode, true) == true
    +
    321  && mkdir(dirpath, mode) == 0) {
    +
    322  ret = true;
    +
    323  }
    +
    324  free(parentpath);
    +
    325  }
    +
    326 
    +
    327  return ret;
    +
    328 }
    +
    329 
    +
    330 /**
    +
    331  * Check path string contains invalid characters.
    +
    332  *
    +
    333  * @param path path string
    +
    334  *
    +
    335  * @return true if ok, otherwise returns false.
    +
    336  */
    +
    337 bool qfile_check_path(const char *path) {
    +
    338  if (path == NULL)
    +
    339  return false;
    +
    340 
    +
    341  int nLen = strlen(path);
    +
    342  if (nLen == 0 || nLen >= PATH_MAX)
    +
    343  return false;
    +
    344  else if (strpbrk(path, "\\:*?\"<>|") != NULL)
    +
    345  return false;
    +
    346  return true;
    +
    347 }
    +
    348 
    +
    349 /**
    +
    350  * Get filename from filepath
    +
    351  *
    +
    352  * @param filepath file or directory path
    +
    353  *
    +
    354  * @return malloced filename string
    +
    355  */
    +
    356 char *qfile_get_name(const char *filepath) {
    +
    357  char *path = strdup(filepath);
    +
    358  char *bname = basename(path);
    +
    359  char *filename = strdup(bname);
    +
    360  free(path);
    +
    361  return filename;
    +
    362 }
    +
    363 
    +
    364 /**
    +
    365  * Get directory suffix from filepath
    +
    366  *
    +
    367  * @param filepath file or directory path
    +
    368  *
    +
    369  * @return malloced filepath string
    +
    370  */
    +
    371 char *qfile_get_dir(const char *filepath) {
    +
    372  char *path = strdup(filepath);
    +
    373  char *dname = dirname(path);
    +
    374  char *dir = strdup(dname);
    +
    375  free(path);
    +
    376  return dir;
    +
    377 }
    +
    378 
    +
    379 /**
    +
    380  * Get extension from filepath.
    +
    381  *
    +
    382  * @param filepath file or directory path
    +
    383  *
    +
    384  * @return malloced extension string which is converted to lower case.
    +
    385  */
    +
    386 char *qfile_get_ext(const char *filepath) {
    +
    387 #define MAX_EXTENSION_LENGTH (8)
    +
    388  char *filename = qfile_get_name(filepath);
    +
    389  char *p = strrchr(filename, '.');
    +
    390  char *ext = NULL;
    +
    391  if (p != NULL && strlen(p + 1) <= MAX_EXTENSION_LENGTH
    +
    392  && qstrtest(isalnum, p + 1) == true) {
    +
    393  ext = strdup(p + 1);
    +
    394  qstrlower(ext);
    +
    395  } else {
    +
    396  ext = strdup("");
    +
    397  }
    +
    398 
    +
    399  free(filename);
    +
    400  return ext;
    +
    401 }
    +
    402 
    +
    403 /**
    +
    404  * Get file size.
    +
    405  *
    +
    406  * @param filepath file or directory path
    +
    407  *
    +
    408  * @return the file size if exists, otherwise returns -1.
    +
    409  */
    +
    410 off_t qfile_get_size(const char *filepath) {
    +
    411  struct stat finfo;
    +
    412  if (stat(filepath, &finfo) < 0)
    +
    413  return -1;
    +
    414  return finfo.st_size;
    +
    415 }
    +
    416 
    +
    417 /**
    +
    418  * Correct path string.
    +
    419  *
    +
    420  * @param path path string
    +
    421  *
    +
    422  * @return path string pointer
    +
    423  *
    +
    424  * @note
    +
    425  * This modify path argument itself.
    +
    426  *
    +
    427  * @code
    +
    428  * "/hello//my/../world" => "/hello/world"
    +
    429  * "././././hello/./world" => "./hello/world"
    +
    430  * "/../hello//world" => "/hello/world"
    +
    431  * "/../hello//world/" => "/hello/world"
    +
    432  * @endcode
    +
    433  */
    +
    434 char *qfile_correct_path(char *path) {
    +
    435  if (path == NULL)
    +
    436  return NULL;
    +
    437 
    +
    438  // take off heading & tailing white spaces
    +
    439  qstrtrim(path);
    +
    440 
    +
    441  while (true) {
    +
    442  // take off double slashes
    +
    443  if (strstr(path, "//") != NULL) {
    +
    444  qstrreplace("sr", path, "//", "/");
    +
    445  continue;
    +
    446  }
    +
    447 
    +
    448  if (strstr(path, "/./") != NULL) {
    +
    449  qstrreplace("sr", path, "/./", "/");
    +
    450  continue;
    +
    451  }
    +
    452 
    +
    453  if (strstr(path, "/../") != NULL) {
    +
    454  char *pszTmp = strstr(path, "/../");
    +
    455  if (pszTmp == path) {
    +
    456  // /../hello => /hello
    +
    457  strcpy(path, pszTmp + 3);
    +
    458  } else {
    +
    459  // /hello/../world => /world
    +
    460  *pszTmp = '\0';
    +
    461  char *pszNewPrefix = qfile_get_dir(path);
    +
    462  strcpy(path, pszNewPrefix);
    +
    463  strcat(path, pszTmp + 3);
    +
    464  free(pszNewPrefix);
    +
    465  }
    +
    466  continue;
    +
    467  }
    +
    468 
    +
    469  // take off tailing slash
    +
    470  size_t nLen = strlen(path);
    +
    471  if (nLen > 1) {
    +
    472  if (path[nLen - 1] == '/') {
    +
    473  path[nLen - 1] = '\0';
    +
    474  continue;
    +
    475  }
    +
    476  }
    +
    477 
    +
    478  // take off tailing /.
    +
    479  if (nLen > 2) {
    +
    480  if (!strcmp(path + (nLen - 2), "/.")) {
    +
    481  path[nLen - 2] = '\0';
    +
    482  continue;
    +
    483  }
    +
    484  }
    +
    485 
    +
    486  // take off tailing /.
    +
    487  if (nLen > 2) {
    +
    488  if (!strcmp(path + (nLen - 2), "/.")) {
    +
    489  path[nLen - 2] = '\0';
    +
    490  continue;
    +
    491  }
    +
    492  }
    +
    493 
    +
    494  // take off tailing /.
    +
    495  if (nLen > 3) {
    +
    496  if (!strcmp(path + (nLen - 3), "/..")) {
    +
    497  path[nLen - 3] = '\0';
    +
    498  char *pszNewPath = qfile_get_dir(path);
    +
    499  strcpy(path, pszNewPath);
    +
    500  free(pszNewPath);
    +
    501  continue;
    +
    502  }
    +
    503  }
    +
    504 
    +
    505  break;
    +
    506  }
    +
    507 
    +
    508  return path;
    +
    509 }
    +
    510 
    +
    511 /**
    +
    512  * Make full absolute system path string.
    +
    513  *
    +
    514  * @param buf buffer string pointer
    +
    515  * @param bufsize buffer size
    +
    516  * @param path path string
    +
    517  *
    +
    518  * @return buffer pointer if successful, otherwise returns NULL.
    +
    519  *
    +
    520  * @code
    +
    521  * char buf[PATH_MAX];
    +
    522  * chdir("/usr/local");
    +
    523  * qfile_abspath(buf, sizeof(buf), "."); // returns "/usr/local"
    +
    524  * qfile_abspath(buf, sizeof(buf), ".."); // returns "/usr"
    +
    525  * qfile_abspath(buf, sizeof(buf), "etc"); // returns "/usr/local/etc"
    +
    526  * qfile_abspath(buf, sizeof(buf), "/etc"); // returns "/etc"
    +
    527  * qfile_abspath(buf, sizeof(buf), "/etc/"); // returns "/etc/"
    +
    528  * @endcode
    +
    529  */
    +
    530 char *qfile_abspath(char *buf, size_t bufsize, const char *path) {
    +
    531  if (bufsize == 0)
    +
    532  return NULL;
    +
    533 
    +
    534  if (path[0] == '/') {
    +
    535  qstrcpy(buf, bufsize, path);
    +
    536  } else {
    +
    537  if (getcwd(buf, bufsize) == NULL)
    +
    538  return NULL;
    +
    539  strcat(buf, "/");
    +
    540  strcat(buf, path);
    +
    541  }
    +
    542  qfile_correct_path(buf);
    +
    543 
    +
    544  return buf;
    +
    545 }
    +
    char * qfile_get_name(const char *filepath)
    Get filename from filepath.
    Definition: qfile.c:356
    +
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition: qfile.c:159
    +
    char * qfile_abspath(char *buf, size_t bufsize, const char *path)
    Make full absolute system path string.
    Definition: qfile.c:530
    +
    char * qfile_correct_path(char *path)
    Correct path string.
    Definition: qfile.c:434
    +
    bool qfile_unlock(int fd)
    Unlock file which is locked by qfile_lock()
    Definition: qfile.c:98
    +
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition: qfile.c:214
    +
    ssize_t qfile_save(const char *filepath, const void *buf, size_t size, bool append)
    Save data into file.
    Definition: qfile.c:283
    +
    bool qfile_mkdir(const char *dirpath, mode_t mode, bool recursive)
    Attempts to create a directory recursively.
    Definition: qfile.c:312
    +
    char * qfile_get_ext(const char *filepath)
    Get extension from filepath.
    Definition: qfile.c:386
    +
    bool qfile_lock(int fd)
    Lock file.
    Definition: qfile.c:74
    +
    char * qfile_get_dir(const char *filepath)
    Get directory suffix from filepath.
    Definition: qfile.c:371
    +
    bool qfile_exist(const char *filepath)
    Check file existence.
    Definition: qfile.c:122
    +
    bool qfile_check_path(const char *path)
    Check path string contains invalid characters.
    Definition: qfile.c:337
    +
    off_t qfile_get_size(const char *filepath)
    Get file size.
    Definition: qfile.c:410
    +
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition: qstring.c:236
    +
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition: qstring.c:752
    +
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition: qstring.c:325
    +
    char * qstrlower(char *str)
    Convert character to lower character.
    Definition: qstring.c:543
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    diff --git a/doc/html/qgrow_8c.html b/doc/html/qgrow_8c.html index ea62b10b..54013590 100644 --- a/doc/html/qgrow_8c.html +++ b/doc/html/qgrow_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qgrow.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qgrow.c File Reference
    +
    +
    qgrow.c File Reference
    @@ -70,47 +70,47 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - + - - - - - - + + + + + + - + - + - +

    +

    Functions

    qgrow_t * qgrow (int options)
     Initialize grow.
     
    qgrow_t * qgrow (int options)
     Initialize grow. More...
     
    bool qgrow_add (qgrow_t *grow, const void *data, size_t size)
     qgrow->add(): Stack object
     qgrow->add(): Stack object More...
     
    bool qgrow_addstr (qgrow_t *grow, const char *str)
     qgrow->addstr(): Stack string
     qgrow->addstr(): Stack string More...
     
    bool qgrow_addstrf (qgrow_t *grow, const char *format,...)
     qgrow->addstrf(): Stack formatted string
     qgrow->addstrf(): Stack formatted string More...
     
    size_t qgrow_size (qgrow_t *grow)
     qgrow->size(): Returns the number of elements in this grow.
     qgrow->size(): Returns the number of elements in this grow. More...
     
    size_t qgrow_datasize (qgrow_t *grow)
     qgrow->datasize(): Returns the sum of total element size in this grow.
     qgrow->datasize(): Returns the sum of total element size in this grow. More...
     
    void * qgrow_toarray (qgrow_t *grow, size_t *size)
     qgrow->toarray(): Returns the serialized chunk containing all the elements in this grow.
     
    char * qgrow_tostring (qgrow_t *grow)
     qgrow->tostring(): Returns a string representation of this grow, containing string representation of each element.
     
    void * qgrow_toarray (qgrow_t *grow, size_t *size)
     qgrow->toarray(): Returns the serialized chunk containing all the elements in this grow. More...
     
    char * qgrow_tostring (qgrow_t *grow)
     qgrow->tostring(): Returns a string representation of this grow, containing string representation of each element. More...
     
    void qgrow_clear (qgrow_t *grow)
     qgrow->clear(): Removes all of the elements from this grow.
     qgrow->clear(): Removes all of the elements from this grow. More...
     
    bool qgrow_debug (qgrow_t *grow, FILE *out)
     qgrow->debug(): Print out stored elements for debugging purpose.
     qgrow->debug(): Print out stored elements for debugging purpose. More...
     
    void qgrow_free (qgrow_t *grow)
     qgrow->free(): De-allocate grow
     qgrow->free(): De-allocate grow More...
     

    Detailed Description

    Grow container that handles growable objects.

    qgrow container is a grow implementation. It implements a growable array of objects and it extends qlist container that allow a linked-list to be treated as a grow.

    [Code sample - Object]
    -
    qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    +
    qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    // add elements
    grow->addstr(grow, "AB"); // no need to supply size
    @@ -131,7 +131,7 @@
    [Result]
    Number of elements = 3
    Final string = AB12CD
    -
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition qgrow.c:134
    +
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition: qgrow.c:134
    [Code sample - Object]
    // sample object
    struct sampleobj {
    @@ -140,7 +140,7 @@
    };
    // get new grow
    -
    qgrow_t *grow = qgrow();
    +
    qgrow_t *grow = qgrow();
    // add objects
    int i;
    @@ -176,14 +176,14 @@

    Definition in file qgrow.c.

    Function Documentation

    - -

    ◆ qgrow()

    + +

    ◆ qgrow()

    - + @@ -210,7 +210,7 @@

    // allocate memory
    -
    qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    +
    qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    grow->free(grow);
    Note
    Available options:
    • QGROW_THREADSAFE - make it thread-safe.
    • @@ -221,8 +221,8 @@

      -

      ◆ qgrow_add()

      + +

      ◆ qgrow_add()

      @@ -278,8 +278,8 @@

      -

      ◆ qgrow_addstr()

      + +

      ◆ qgrow_addstr()

      @@ -328,8 +328,8 @@

      -

      ◆ qgrow_addstrf()

      + +

      ◆ qgrow_addstrf()

      @@ -384,8 +384,8 @@

      -

      ◆ qgrow_size()

      + +

      ◆ qgrow_size()

      @@ -413,8 +413,8 @@

      -

      ◆ qgrow_datasize()

      + +

      ◆ qgrow_datasize()

      @@ -442,14 +442,14 @@

      -

      ◆ qgrow_toarray()

      + +

      ◆ qgrow_toarray()

    qgrow_t * qgrow qgrow_t* qgrow ( int  options)
    - + @@ -492,14 +492,14 @@

    -

    ◆ qgrow_tostring()

    + +

    ◆ qgrow_tostring()

    void * qgrow_toarray void* qgrow_toarray ( qgrow_t *  grow,
    - + @@ -532,8 +532,8 @@

    -

    ◆ qgrow_clear()

    + +

    ◆ qgrow_clear()

    @@ -560,8 +560,8 @@

    -

    ◆ qgrow_debug()

    + +

    ◆ qgrow_debug()

    @@ -609,8 +609,8 @@

    -

    ◆ qgrow_free()

    + +

    ◆ qgrow_free()

    @@ -643,7 +643,7 @@

    diff --git a/doc/html/qgrow_8c.js b/doc/html/qgrow_8c.js index 254c82a5..d643d990 100644 --- a/doc/html/qgrow_8c.js +++ b/doc/html/qgrow_8c.js @@ -1,13 +1,13 @@ var qgrow_8c = [ - [ "qgrow", "qgrow_8c.html#a4970eee21ac920bc459941c15c566866", null ], + [ "qgrow", "qgrow_8c.html#a3a0165e2eba9d9098f2e448791e8aae9", null ], [ "qgrow_add", "qgrow_8c.html#a313f8b8da26b36f9558525efe34cda29", null ], [ "qgrow_addstr", "qgrow_8c.html#a0e4759834172363db2e63799da00696b", null ], [ "qgrow_addstrf", "qgrow_8c.html#a6c52a8b3243100e3f2b7a97f47b6b992", null ], [ "qgrow_size", "qgrow_8c.html#a3e37ab3040be6523fc93a135aea45949", null ], [ "qgrow_datasize", "qgrow_8c.html#aec52e4932ddb3e1f29b95540f2231ac7", null ], - [ "qgrow_toarray", "qgrow_8c.html#a8df097303be8f8d1408a17cd7ee7c106", null ], - [ "qgrow_tostring", "qgrow_8c.html#a74e599b92cd949f803bb17ab9fe1b914", null ], + [ "qgrow_toarray", "qgrow_8c.html#af8dcd5f0d8fce7d4c9d1727076823019", null ], + [ "qgrow_tostring", "qgrow_8c.html#ab1a0aaa7efbdbb40bac079d6ad103a8e", null ], [ "qgrow_clear", "qgrow_8c.html#a748f12b95d3e7f199e77a503070fe62b", null ], [ "qgrow_debug", "qgrow_8c.html#a093d851a279a44373605d7c57d498d4c", null ], [ "qgrow_free", "qgrow_8c.html#a00f87daf5492d6a042fca6e310086560", null ] diff --git a/doc/html/qgrow_8c_source.html b/doc/html/qgrow_8c_source.html index ca414c35..36f00c37 100644 --- a/doc/html/qgrow_8c_source.html +++ b/doc/html/qgrow_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qgrow.c Source File @@ -20,8 +20,8 @@

    char * qgrow_tostring char* qgrow_tostring ( qgrow_t *  grow)
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,345 +52,346 @@
    -
    qgrow.c
    +
    +
    qgrow.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qgrow.c Grow container that handles growable objects.
    -
    31 *
    -
    32 * qgrow container is a grow implementation. It implements a growable array
    -
    33 * of objects and it extends qlist container that allow a linked-list to be
    -
    34 * treated as a grow.
    -
    35 *
    -
    36 * @code
    -
    37 * [Code sample - Object]
    -
    38 * qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    -
    39 *
    -
    40 * // add elements
    -
    41 * grow->addstr(grow, "AB"); // no need to supply size
    -
    42 * grow->addstrf(grow, "%d", 12); // for formatted string
    -
    43 * grow->addstr(grow, "CD");
    -
    44 *
    -
    45 * // get the chunk as a string
    -
    46 * char *final = grow->tostring(grow);
    -
    47 *
    -
    48 * // print out
    -
    49 * printf("Number of elements = %zu\n", grow->size(grow));
    -
    50 * printf("Final string = %s\n", final);
    -
    51 *
    -
    52 * // release
    -
    53 * free(final);
    -
    54 * grow->free(grow);
    -
    55 *
    -
    56 * [Result]
    -
    57 * Number of elements = 3
    -
    58 * Final string = AB12CD
    -
    59 * @endcode
    -
    60 *
    -
    61 * @code
    -
    62 * [Code sample - Object]
    -
    63 * // sample object
    -
    64 * struct sampleobj {
    -
    65 * int num;
    -
    66 * char str[10];
    -
    67 * };
    -
    68 *
    -
    69 * // get new grow
    -
    70 * qgrow_t *grow = qgrow();
    -
    71 *
    -
    72 * // add objects
    -
    73 * int i;
    -
    74 * struct sampleobj obj;
    -
    75 * for(i = 0; i < 3; i++) {
    -
    76 * // filling object with sample data
    -
    77 * obj.num = i;
    -
    78 * sprintf(obj.str, "hello%d", i);
    -
    79 *
    -
    80 * // stack
    -
    81 * grow->add(grow, (void *)&obj, sizeof(struct sampleobj));
    -
    82 * }
    -
    83 *
    -
    84 * // final
    -
    85 * struct sampleobj *final;
    -
    86 * final = (struct sampleobj *)grow->toarray(grow, NULL);
    -
    87 *
    -
    88 * // print out
    -
    89 * printf("Number of Objects = %zu\n", grow->size(grow));
    -
    90 * for(i = 0; i < grow->size(grow); i++) {
    -
    91 * printf("Object%d %d, %s\n", i+1, final[i].num, final[i].str);
    -
    92 * }
    -
    93 *
    -
    94 * // release
    -
    95 * free(final);
    -
    96 * grow->free(grow);
    -
    97 *
    -
    98 * [Result]
    -
    99 * Number of Objects = 3
    -
    100 * Object1 0, hello0
    -
    101 * Object2 1, hello1
    -
    102 * Object3 2, hello2
    -
    103 * @endcode
    -
    104 */
    -
    105
    -
    106#include <stdio.h>
    -
    107#include <stdlib.h>
    -
    108#include <stdbool.h>
    -
    109#include <stdarg.h>
    -
    110#include <string.h>
    -
    111#include <errno.h>
    -
    112#include "qinternal.h"
    -
    113#include "containers/qgrow.h"
    -
    114
    -
    115/**
    -
    116 * Initialize grow.
    -
    117 *
    -
    118 * @param options combination of initialization options.
    -
    119 *
    -
    120 * @return qgrow_t container pointer.
    -
    121 * @retval errno will be set in error condition.
    -
    122 * - ENOMEM : Memory allocation failure.
    -
    123 *
    -
    124 * @code
    -
    125 * // allocate memory
    -
    126 * qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    -
    127 * grow->free(grow);
    -
    128 * @endcode
    -
    129 *
    -
    130 * @note
    -
    131 * Available options:
    -
    132 * - QGROW_THREADSAFE - make it thread-safe.
    -
    133 */
    -
    134qgrow_t *qgrow(int options) {
    -
    135 qgrow_t *grow = (qgrow_t *) calloc(1, sizeof(qgrow_t));
    -
    136 if (grow == NULL) {
    -
    137 errno = ENOMEM;
    -
    138 return NULL;
    -
    139 }
    -
    140
    -
    141 grow->list = qlist(options);
    -
    142 if (grow->list == NULL) {
    -
    143 free(grow);
    -
    144 errno = ENOMEM;
    -
    145 return NULL;
    -
    146 }
    -
    147
    -
    148 // methods
    -
    149 grow->add = qgrow_add;
    -
    150 grow->addstr = qgrow_addstr;
    -
    151 grow->addstrf = qgrow_addstrf;
    -
    152
    -
    153 grow->size = qgrow_size;
    -
    154 grow->datasize = qgrow_datasize;
    -
    155
    -
    156 grow->toarray = qgrow_toarray;
    -
    157 grow->tostring = qgrow_tostring;
    -
    158
    -
    159 grow->clear = qgrow_clear;
    -
    160 grow->debug = qgrow_debug;
    -
    161 grow->free = qgrow_free;
    -
    162
    -
    163 return grow;
    -
    164}
    -
    165
    -
    166/**
    -
    167 * qgrow->add(): Stack object
    -
    168 *
    -
    169 * @param grow qgrow_t container pointer.
    -
    170 * @param object a pointer of object data
    -
    171 * @param size size of object
    -
    172 *
    -
    173 * @return true if successful, otherwise returns false
    -
    174 * @retval errno will be set in error condition.
    -
    175 * - EINVAL : Invalid argument.
    -
    176 * - ENOMEM : Memory allocation failure.
    -
    177 */
    -
    178bool qgrow_add(qgrow_t *grow, const void *data, size_t size) {
    -
    179 return grow->list->addlast(grow->list, data, size);
    -
    180}
    -
    181
    -
    182/**
    -
    183 * qgrow->addstr(): Stack string
    -
    184 *
    -
    185 * @param grow qgrow_t container pointer.
    -
    186 * @param str a pointer of string
    -
    187 *
    -
    188 * @return true if successful, otherwise returns false
    -
    189 * @retval errno will be set in error condition.
    -
    190 * - EINVAL : Invalid argument.
    -
    191 * - ENOMEM : Memory allocation failure.
    -
    192 */
    -
    193bool qgrow_addstr(qgrow_t *grow, const char *str) {
    -
    194 return grow->list->addlast(grow->list, str, strlen(str));
    -
    195}
    -
    196
    -
    197/**
    -
    198 * qgrow->addstrf(): Stack formatted string
    -
    199 *
    -
    200 * @param grow qgrow_t container pointer.
    -
    201 * @param format string format
    -
    202 *
    -
    203 * @return true if successful, otherwise returns false
    -
    204 * @retval errno will be set in error condition.
    -
    205 * - EINVAL : Invalid argument.
    -
    206 * - ENOMEM : Memory allocation failure.
    -
    207 */
    -
    208bool qgrow_addstrf(qgrow_t *grow, const char *format, ...) {
    -
    209 char *str;
    -
    210 DYNAMIC_VSPRINTF(str, format);
    -
    211 if (str == NULL) {
    -
    212 errno = ENOMEM;
    -
    213 return false;
    -
    214 }
    -
    215
    -
    216 bool ret = qgrow_addstr(grow, str);
    -
    217 free(str);
    -
    218
    -
    219 return ret;
    -
    220}
    -
    221
    -
    222/**
    -
    223 * qgrow->size(): Returns the number of elements in this grow.
    -
    224 *
    -
    225 * @param grow qgrow_t container pointer.
    -
    226 *
    -
    227 * @return the number of elements in this grow.
    -
    228 */
    -
    229size_t qgrow_size(qgrow_t *grow) {
    -
    230 return grow->list->size(grow->list);
    -
    231}
    -
    232
    -
    233/**
    -
    234 * qgrow->datasize(): Returns the sum of total element size in this
    -
    235 * grow.
    -
    236 *
    -
    237 * @param grow qgrow_t container pointer.
    -
    238 *
    -
    239 * @return the sum of total element size in this grow.
    -
    240 */
    -
    241size_t qgrow_datasize(qgrow_t *grow) {
    -
    242 return grow->list->datasize(grow->list);
    -
    243}
    -
    244
    -
    245/**
    -
    246 * qgrow->toarray(): Returns the serialized chunk containing all the
    -
    247 * elements in this grow.
    -
    248 *
    -
    249 * @param grow qgrow_t container pointer.
    -
    250 * @param size if size is not NULL, merged object size will be stored.
    -
    251 *
    -
    252 * @return a pointer of finally merged elements(malloced), otherwise returns
    -
    253 * NULL
    -
    254 * @retval errno will be set in error condition.
    -
    255 * - ENOENT : empty.
    -
    256 * - ENOMEM : Memory allocation failure.
    -
    257 */
    -
    258void *qgrow_toarray(qgrow_t *grow, size_t *size) {
    -
    259 return grow->list->toarray(grow->list, size);
    -
    260}
    -
    261
    -
    262/**
    -
    263 * qgrow->tostring(): Returns a string representation of this grow,
    -
    264 * containing string representation of each element.
    -
    265 *
    -
    266 * @param grow qgrow_t container pointer.
    -
    267 *
    -
    268 * @return a pointer of finally merged strings(malloced), otherwise returns NULL
    -
    269 * @retval errno will be set in error condition.
    -
    270 * - ENOENT : empty.
    -
    271 * - ENOMEM : Memory allocation failure.
    -
    272 *
    -
    273 * @note
    -
    274 * Return string is always terminated by '\0'.
    -
    275 */
    -
    276char *qgrow_tostring(qgrow_t *grow) {
    -
    277 return grow->list->tostring(grow->list);
    -
    278}
    -
    279
    -
    280/**
    -
    281 * qgrow->clear(): Removes all of the elements from this grow.
    -
    282 *
    -
    283 * @param grow qgrow_t container pointer.
    -
    284 */
    -
    285void qgrow_clear(qgrow_t *grow) {
    -
    286 grow->list->clear(grow->list);
    -
    287}
    -
    288
    -
    289/**
    -
    290 * qgrow->debug(): Print out stored elements for debugging purpose.
    -
    291 *
    -
    292 * @param grow qgrow_t container pointer.
    -
    293 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    294 *
    -
    295 * @return true if successful, otherwise returns false.
    -
    296 * @retval errno will be set in error condition.
    -
    297 * - EIO : Invalid output stream.
    -
    298 */
    -
    299bool qgrow_debug(qgrow_t *grow, FILE *out) {
    -
    300 return grow->list->debug(grow->list, out);
    -
    301}
    -
    302
    -
    303/**
    -
    304 * qgrow->free(): De-allocate grow
    -
    305 *
    -
    306 * @param grow qgrow_t container pointer.
    -
    307 */
    -
    308void qgrow_free(qgrow_t *grow) {
    -
    309 grow->list->free(grow->list);
    -
    310 free(grow);
    -
    311}
    -
    void qgrow_free(qgrow_t *grow)
    qgrow->free(): De-allocate grow
    Definition qgrow.c:308
    -
    bool qgrow_debug(qgrow_t *grow, FILE *out)
    qgrow->debug(): Print out stored elements for debugging purpose.
    Definition qgrow.c:299
    -
    bool qgrow_addstr(qgrow_t *grow, const char *str)
    qgrow->addstr(): Stack string
    Definition qgrow.c:193
    -
    bool qgrow_add(qgrow_t *grow, const void *data, size_t size)
    qgrow->add(): Stack object
    Definition qgrow.c:178
    -
    size_t qgrow_size(qgrow_t *grow)
    qgrow->size(): Returns the number of elements in this grow.
    Definition qgrow.c:229
    -
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition qgrow.c:134
    -
    bool qgrow_addstrf(qgrow_t *grow, const char *format,...)
    qgrow->addstrf(): Stack formatted string
    Definition qgrow.c:208
    -
    void qgrow_clear(qgrow_t *grow)
    qgrow->clear(): Removes all of the elements from this grow.
    Definition qgrow.c:285
    -
    char * qgrow_tostring(qgrow_t *grow)
    qgrow->tostring(): Returns a string representation of this grow, containing string representation of ...
    Definition qgrow.c:276
    -
    void * qgrow_toarray(qgrow_t *grow, size_t *size)
    qgrow->toarray(): Returns the serialized chunk containing all the elements in this grow.
    Definition qgrow.c:258
    -
    size_t qgrow_datasize(qgrow_t *grow)
    qgrow->datasize(): Returns the sum of total element size in this grow.
    Definition qgrow.c:241
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qgrow.c Grow container that handles growable objects.
    +
    31  *
    +
    32  * qgrow container is a grow implementation. It implements a growable array
    +
    33  * of objects and it extends qlist container that allow a linked-list to be
    +
    34  * treated as a grow.
    +
    35  *
    +
    36  * @code
    +
    37  * [Code sample - Object]
    +
    38  * qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    +
    39  *
    +
    40  * // add elements
    +
    41  * grow->addstr(grow, "AB"); // no need to supply size
    +
    42  * grow->addstrf(grow, "%d", 12); // for formatted string
    +
    43  * grow->addstr(grow, "CD");
    +
    44  *
    +
    45  * // get the chunk as a string
    +
    46  * char *final = grow->tostring(grow);
    +
    47  *
    +
    48  * // print out
    +
    49  * printf("Number of elements = %zu\n", grow->size(grow));
    +
    50  * printf("Final string = %s\n", final);
    +
    51  *
    +
    52  * // release
    +
    53  * free(final);
    +
    54  * grow->free(grow);
    +
    55  *
    +
    56  * [Result]
    +
    57  * Number of elements = 3
    +
    58  * Final string = AB12CD
    +
    59  * @endcode
    +
    60  *
    +
    61  * @code
    +
    62  * [Code sample - Object]
    +
    63  * // sample object
    +
    64  * struct sampleobj {
    +
    65  * int num;
    +
    66  * char str[10];
    +
    67  * };
    +
    68  *
    +
    69  * // get new grow
    +
    70  * qgrow_t *grow = qgrow();
    +
    71  *
    +
    72  * // add objects
    +
    73  * int i;
    +
    74  * struct sampleobj obj;
    +
    75  * for(i = 0; i < 3; i++) {
    +
    76  * // filling object with sample data
    +
    77  * obj.num = i;
    +
    78  * sprintf(obj.str, "hello%d", i);
    +
    79  *
    +
    80  * // stack
    +
    81  * grow->add(grow, (void *)&obj, sizeof(struct sampleobj));
    +
    82  * }
    +
    83  *
    +
    84  * // final
    +
    85  * struct sampleobj *final;
    +
    86  * final = (struct sampleobj *)grow->toarray(grow, NULL);
    +
    87  *
    +
    88  * // print out
    +
    89  * printf("Number of Objects = %zu\n", grow->size(grow));
    +
    90  * for(i = 0; i < grow->size(grow); i++) {
    +
    91  * printf("Object%d %d, %s\n", i+1, final[i].num, final[i].str);
    +
    92  * }
    +
    93  *
    +
    94  * // release
    +
    95  * free(final);
    +
    96  * grow->free(grow);
    +
    97  *
    +
    98  * [Result]
    +
    99  * Number of Objects = 3
    +
    100  * Object1 0, hello0
    +
    101  * Object2 1, hello1
    +
    102  * Object3 2, hello2
    +
    103  * @endcode
    +
    104  */
    +
    105 
    +
    106 #include <stdio.h>
    +
    107 #include <stdlib.h>
    +
    108 #include <stdbool.h>
    +
    109 #include <stdarg.h>
    +
    110 #include <string.h>
    +
    111 #include <errno.h>
    +
    112 #include "qinternal.h"
    +
    113 #include "containers/qgrow.h"
    +
    114 
    +
    115 /**
    +
    116  * Initialize grow.
    +
    117  *
    +
    118  * @param options combination of initialization options.
    +
    119  *
    +
    120  * @return qgrow_t container pointer.
    +
    121  * @retval errno will be set in error condition.
    +
    122  * - ENOMEM : Memory allocation failure.
    +
    123  *
    +
    124  * @code
    +
    125  * // allocate memory
    +
    126  * qgrow_t *grow = qgrow(QGROW_THREADSAFE);
    +
    127  * grow->free(grow);
    +
    128  * @endcode
    +
    129  *
    +
    130  * @note
    +
    131  * Available options:
    +
    132  * - QGROW_THREADSAFE - make it thread-safe.
    +
    133  */
    +
    134 qgrow_t *qgrow(int options) {
    +
    135  qgrow_t *grow = (qgrow_t *) calloc(1, sizeof(qgrow_t));
    +
    136  if (grow == NULL) {
    +
    137  errno = ENOMEM;
    +
    138  return NULL;
    +
    139  }
    +
    140 
    +
    141  grow->list = qlist(options);
    +
    142  if (grow->list == NULL) {
    +
    143  free(grow);
    +
    144  errno = ENOMEM;
    +
    145  return NULL;
    +
    146  }
    +
    147 
    +
    148  // methods
    +
    149  grow->add = qgrow_add;
    +
    150  grow->addstr = qgrow_addstr;
    +
    151  grow->addstrf = qgrow_addstrf;
    +
    152 
    +
    153  grow->size = qgrow_size;
    +
    154  grow->datasize = qgrow_datasize;
    +
    155 
    +
    156  grow->toarray = qgrow_toarray;
    +
    157  grow->tostring = qgrow_tostring;
    +
    158 
    +
    159  grow->clear = qgrow_clear;
    +
    160  grow->debug = qgrow_debug;
    +
    161  grow->free = qgrow_free;
    +
    162 
    +
    163  return grow;
    +
    164 }
    +
    165 
    +
    166 /**
    +
    167  * qgrow->add(): Stack object
    +
    168  *
    +
    169  * @param grow qgrow_t container pointer.
    +
    170  * @param object a pointer of object data
    +
    171  * @param size size of object
    +
    172  *
    +
    173  * @return true if successful, otherwise returns false
    +
    174  * @retval errno will be set in error condition.
    +
    175  * - EINVAL : Invalid argument.
    +
    176  * - ENOMEM : Memory allocation failure.
    +
    177  */
    +
    178 bool qgrow_add(qgrow_t *grow, const void *data, size_t size) {
    +
    179  return grow->list->addlast(grow->list, data, size);
    +
    180 }
    +
    181 
    +
    182 /**
    +
    183  * qgrow->addstr(): Stack string
    +
    184  *
    +
    185  * @param grow qgrow_t container pointer.
    +
    186  * @param str a pointer of string
    +
    187  *
    +
    188  * @return true if successful, otherwise returns false
    +
    189  * @retval errno will be set in error condition.
    +
    190  * - EINVAL : Invalid argument.
    +
    191  * - ENOMEM : Memory allocation failure.
    +
    192  */
    +
    193 bool qgrow_addstr(qgrow_t *grow, const char *str) {
    +
    194  return grow->list->addlast(grow->list, str, strlen(str));
    +
    195 }
    +
    196 
    +
    197 /**
    +
    198  * qgrow->addstrf(): Stack formatted string
    +
    199  *
    +
    200  * @param grow qgrow_t container pointer.
    +
    201  * @param format string format
    +
    202  *
    +
    203  * @return true if successful, otherwise returns false
    +
    204  * @retval errno will be set in error condition.
    +
    205  * - EINVAL : Invalid argument.
    +
    206  * - ENOMEM : Memory allocation failure.
    +
    207  */
    +
    208 bool qgrow_addstrf(qgrow_t *grow, const char *format, ...) {
    +
    209  char *str;
    +
    210  DYNAMIC_VSPRINTF(str, format);
    +
    211  if (str == NULL) {
    +
    212  errno = ENOMEM;
    +
    213  return false;
    +
    214  }
    +
    215 
    +
    216  bool ret = qgrow_addstr(grow, str);
    +
    217  free(str);
    +
    218 
    +
    219  return ret;
    +
    220 }
    +
    221 
    +
    222 /**
    +
    223  * qgrow->size(): Returns the number of elements in this grow.
    +
    224  *
    +
    225  * @param grow qgrow_t container pointer.
    +
    226  *
    +
    227  * @return the number of elements in this grow.
    +
    228  */
    +
    229 size_t qgrow_size(qgrow_t *grow) {
    +
    230  return grow->list->size(grow->list);
    +
    231 }
    +
    232 
    +
    233 /**
    +
    234  * qgrow->datasize(): Returns the sum of total element size in this
    +
    235  * grow.
    +
    236  *
    +
    237  * @param grow qgrow_t container pointer.
    +
    238  *
    +
    239  * @return the sum of total element size in this grow.
    +
    240  */
    +
    241 size_t qgrow_datasize(qgrow_t *grow) {
    +
    242  return grow->list->datasize(grow->list);
    +
    243 }
    +
    244 
    +
    245 /**
    +
    246  * qgrow->toarray(): Returns the serialized chunk containing all the
    +
    247  * elements in this grow.
    +
    248  *
    +
    249  * @param grow qgrow_t container pointer.
    +
    250  * @param size if size is not NULL, merged object size will be stored.
    +
    251  *
    +
    252  * @return a pointer of finally merged elements(malloced), otherwise returns
    +
    253  * NULL
    +
    254  * @retval errno will be set in error condition.
    +
    255  * - ENOENT : empty.
    +
    256  * - ENOMEM : Memory allocation failure.
    +
    257  */
    +
    258 void *qgrow_toarray(qgrow_t *grow, size_t *size) {
    +
    259  return grow->list->toarray(grow->list, size);
    +
    260 }
    +
    261 
    +
    262 /**
    +
    263  * qgrow->tostring(): Returns a string representation of this grow,
    +
    264  * containing string representation of each element.
    +
    265  *
    +
    266  * @param grow qgrow_t container pointer.
    +
    267  *
    +
    268  * @return a pointer of finally merged strings(malloced), otherwise returns NULL
    +
    269  * @retval errno will be set in error condition.
    +
    270  * - ENOENT : empty.
    +
    271  * - ENOMEM : Memory allocation failure.
    +
    272  *
    +
    273  * @note
    +
    274  * Return string is always terminated by '\0'.
    +
    275  */
    +
    276 char *qgrow_tostring(qgrow_t *grow) {
    +
    277  return grow->list->tostring(grow->list);
    +
    278 }
    +
    279 
    +
    280 /**
    +
    281  * qgrow->clear(): Removes all of the elements from this grow.
    +
    282  *
    +
    283  * @param grow qgrow_t container pointer.
    +
    284  */
    +
    285 void qgrow_clear(qgrow_t *grow) {
    +
    286  grow->list->clear(grow->list);
    +
    287 }
    +
    288 
    +
    289 /**
    +
    290  * qgrow->debug(): Print out stored elements for debugging purpose.
    +
    291  *
    +
    292  * @param grow qgrow_t container pointer.
    +
    293  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    294  *
    +
    295  * @return true if successful, otherwise returns false.
    +
    296  * @retval errno will be set in error condition.
    +
    297  * - EIO : Invalid output stream.
    +
    298  */
    +
    299 bool qgrow_debug(qgrow_t *grow, FILE *out) {
    +
    300  return grow->list->debug(grow->list, out);
    +
    301 }
    +
    302 
    +
    303 /**
    +
    304  * qgrow->free(): De-allocate grow
    +
    305  *
    +
    306  * @param grow qgrow_t container pointer.
    +
    307  */
    +
    308 void qgrow_free(qgrow_t *grow) {
    +
    309  grow->list->free(grow->list);
    +
    310  free(grow);
    +
    311 }
    +
    void qgrow_free(qgrow_t *grow)
    qgrow->free(): De-allocate grow
    Definition: qgrow.c:308
    +
    bool qgrow_debug(qgrow_t *grow, FILE *out)
    qgrow->debug(): Print out stored elements for debugging purpose.
    Definition: qgrow.c:299
    +
    bool qgrow_addstr(qgrow_t *grow, const char *str)
    qgrow->addstr(): Stack string
    Definition: qgrow.c:193
    +
    bool qgrow_add(qgrow_t *grow, const void *data, size_t size)
    qgrow->add(): Stack object
    Definition: qgrow.c:178
    +
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition: qgrow.c:134
    +
    size_t qgrow_size(qgrow_t *grow)
    qgrow->size(): Returns the number of elements in this grow.
    Definition: qgrow.c:229
    +
    bool qgrow_addstrf(qgrow_t *grow, const char *format,...)
    qgrow->addstrf(): Stack formatted string
    Definition: qgrow.c:208
    +
    void qgrow_clear(qgrow_t *grow)
    qgrow->clear(): Removes all of the elements from this grow.
    Definition: qgrow.c:285
    +
    char * qgrow_tostring(qgrow_t *grow)
    qgrow->tostring(): Returns a string representation of this grow, containing string representation of ...
    Definition: qgrow.c:276
    +
    size_t qgrow_datasize(qgrow_t *grow)
    qgrow->datasize(): Returns the sum of total element size in this grow.
    Definition: qgrow.c:241
    +
    void * qgrow_toarray(qgrow_t *grow, size_t *size)
    qgrow->toarray(): Returns the serialized chunk containing all the elements in this grow.
    Definition: qgrow.c:258
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124
    diff --git a/doc/html/qhash_8c.html b/doc/html/qhash_8c.html index 9880b89b..a5b4ca06 100644 --- a/doc/html/qhash_8c.html +++ b/doc/html/qhash_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qhash.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@

    @@ -61,7 +60,8 @@
    -
    qhash.c File Reference
    +
    +
    qhash.c File Reference
    @@ -70,25 +70,25 @@

    Go to the source code of this file.

    - - + - + - + - + - + - +

    +

    Functions

    bool qhashmd5 (const void *data, size_t nbytes, void *retbuf)
     Calculate 128-bit(16-bytes) MD5 hash.
     Calculate 128-bit(16-bytes) MD5 hash. More...
     
    bool qhashmd5_file (const char *filepath, off_t offset, ssize_t nbytes, void *retbuf)
     Get 128-bit MD5 hash of a file contents.
     Get 128-bit MD5 hash of a file contents. More...
     
    uint32_t qhashfnv1_32 (const void *data, size_t nbytes)
     Get 32-bit FNV1 hash.
     Get 32-bit FNV1 hash. More...
     
    uint64_t qhashfnv1_64 (const void *data, size_t nbytes)
     Get 64-bit FNV1 hash integer.
     Get 64-bit FNV1 hash integer. More...
     
    uint32_t qhashmurmur3_32 (const void *data, size_t nbytes)
     Get 32-bit Murmur3 hash.
     Get 32-bit Murmur3 hash. More...
     
    bool qhashmurmur3_128 (const void *data, size_t nbytes, void *retbuf)
     Get 128-bit Murmur3 hash.
     Get 128-bit Murmur3 hash. More...
     

    Detailed Description

    @@ -96,8 +96,8 @@

    Definition in file qhash.c.

    Function Documentation

    - -

    ◆ qhashmd5()

    + +

    ◆ qhashmd5()

    @@ -140,21 +140,21 @@

    Returns
    true if successful, otherwise false.
    // get MD5
    unsigned char md5hash[16];
    -
    qhashmd5((void*)"hello", 5, md5hash);
    +
    qhashmd5((void*)"hello", 5, md5hash);
    // hex encode
    -
    char *md5ascii = qhex_encode(md5hash, 16);
    +
    char *md5ascii = qhex_encode(md5hash, 16);
    printf("Hex encoded MD5: %s\n", md5ascii);
    free(md5ascii);
    -
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition qencode.c:393
    -
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition qhash.c:67
    +
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition: qencode.c:393
    +
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition: qhash.c:67

    Definition at line 67 of file qhash.c.

    - -

    ◆ qhashmd5_file()

    + +

    ◆ qhashmd5_file()

    @@ -203,15 +203,15 @@

    Returns
    true if successful, otherwise false.
    unsigned char md5hash[16];
    -
    qhashmd5_file("/tmp/test.dat", 0, 0, md5hash);
    -
    bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes, void *retbuf)
    Get 128-bit MD5 hash of a file contents.
    Definition qhash.c:97
    +
    qhashmd5_file("/tmp/test.dat", 0, 0, md5hash);
    +
    bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes, void *retbuf)
    Get 128-bit MD5 hash of a file contents.
    Definition: qhash.c:97

    Definition at line 97 of file qhash.c.

    - -

    ◆ qhashfnv1_32()

    + +

    ◆ qhashfnv1_32()

    - -

    ◆ qhashfnv1_64()

    + +

    ◆ qhashfnv1_64()

    @@ -310,15 +310,15 @@

    Returns
    64-bit unsigned hash value.
    -
    uint64_t fnv64 = qhashfnv1_64((void*)"hello", 5);
    -
    uint64_t qhashfnv1_64(const void *data, size_t nbytes)
    Get 64-bit FNV1 hash integer.
    Definition qhash.c:223
    +
    uint64_t fnv64 = qhashfnv1_64((void*)"hello", 5);
    +
    uint64_t qhashfnv1_64(const void *data, size_t nbytes)
    Get 64-bit FNV1 hash integer.
    Definition: qhash.c:223

    Definition at line 223 of file qhash.c.

    - -

    ◆ qhashmurmur3_32()

    + +

    ◆ qhashmurmur3_32()

    @@ -352,8 +352,8 @@

    Returns
    32-bit unsigned hash value.
    -
    -
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition qhash.c:263
    +
    uint32_t hashval = qhashmurmur3_32((void*)"hello", 5);
    +
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition: qhash.c:263
    MurmurHash3 was created by Austin Appleby in 2008. The initial
    implementation was published in C++ and placed in the public.
    https://sites.google.com/site/murmurhash/
    @@ -364,8 +364,8 @@

    -

    ◆ qhashmurmur3_128()

    + +

    ◆ qhashmurmur3_128()

    @@ -408,13 +408,13 @@

    Returns
    true if successful, otherwise false.
    // get 128-bit Murmur3 hash.
    unsigned char hash[16];
    -
    qhashmurmur3_128((void*)"hello", 5, hash);
    +
    qhashmurmur3_128((void*)"hello", 5, hash);
    // hex encode
    -
    char *ascii = qhex_encode(hash, 16);
    +
    char *ascii = qhex_encode(hash, 16);
    printf("Hex encoded Murmur3: %s\n", ascii);
    free(ascii);
    -
    bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf)
    Get 128-bit Murmur3 hash.
    Definition qhash.c:335
    +
    bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf)
    Get 128-bit Murmur3 hash.
    Definition: qhash.c:335

    Definition at line 335 of file qhash.c.

    @@ -426,7 +426,7 @@

    diff --git a/doc/html/qhash_8c_source.html b/doc/html/qhash_8c_source.html index a7411de3..4489bb35 100644 --- a/doc/html/qhash_8c_source.html +++ b/doc/html/qhash_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qhash.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,473 +52,474 @@

    -
    qhash.c
    +
    +
    qhash.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qhash.c Hash APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <stdint.h>
    -
    37#include <string.h>
    -
    38#include <unistd.h>
    -
    39#include <fcntl.h>
    -
    40#include <errno.h>
    -
    41#include <sys/types.h>
    -
    42#include <sys/stat.h>
    -
    43#include "md5/md5.h"
    -
    44#include "qinternal.h"
    -
    45#include "utilities/qhash.h"
    -
    46
    -
    47/**
    -
    48 * Calculate 128-bit(16-bytes) MD5 hash.
    -
    49 *
    -
    50 * @param data source object
    -
    51 * @param nbytes size of data
    -
    52 * @param retbuf user buffer. It must be at leat 16-bytes long.
    -
    53 *
    -
    54 * @return true if successful, otherwise false.
    -
    55 *
    -
    56 * @code
    -
    57 * // get MD5
    -
    58 * unsigned char md5hash[16];
    -
    59 * qhashmd5((void*)"hello", 5, md5hash);
    -
    60 *
    -
    61 * // hex encode
    -
    62 * char *md5ascii = qhex_encode(md5hash, 16);
    -
    63 * printf("Hex encoded MD5: %s\n", md5ascii);
    -
    64 * free(md5ascii);
    -
    65 * @endcode
    -
    66 */
    -
    67bool qhashmd5(const void *data, size_t nbytes, void *retbuf) {
    -
    68 if (data == NULL || retbuf == NULL) {
    -
    69 errno = EINVAL;
    -
    70 return false;
    -
    71 }
    -
    72
    -
    73 MD5_CTX context;
    -
    74 MD5Init(&context);
    -
    75 MD5Update(&context, (unsigned char *) data, (unsigned int) nbytes);
    -
    76 MD5Final(retbuf, &context);
    -
    77
    -
    78 return true;
    -
    79}
    -
    80
    -
    81/**
    -
    82 * Get 128-bit MD5 hash of a file contents.
    -
    83 *
    -
    84 * @param filepath file path
    -
    85 * @param offset start offset. Set to 0 to digest from beginning of file.
    -
    86 * @param nbytes number of bytes to digest. Set to 0 to digest until end
    -
    87 * of file.
    -
    88 * @param retbuf user buffer. It must be at leat 16-bytes long.
    -
    89 *
    -
    90 * @return true if successful, otherwise false.
    -
    91 *
    -
    92 * @code
    -
    93 * unsigned char md5hash[16];
    -
    94 * qhashmd5_file("/tmp/test.dat", 0, 0, md5hash);
    -
    95 * @endcode
    -
    96 */
    -
    97bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes,
    -
    98 void *retbuf) {
    -
    99 if (filepath == NULL || offset < 0 || nbytes < 0 || retbuf == NULL) {
    -
    100 errno = EINVAL;
    -
    101 return false;
    -
    102 }
    -
    103
    -
    104 int fd = open(filepath, O_RDONLY, 0);
    -
    105 if (fd < 0)
    -
    106 return false;
    -
    107
    -
    108 struct stat st;
    -
    109 if (fstat(fd, &st) < 0)
    -
    110 return false;
    -
    111 size_t size = st.st_size;
    -
    112
    -
    113 // check filesize
    -
    114 if (size < offset + nbytes) {
    -
    115 errno = EINVAL;
    -
    116 close(fd);
    -
    117 return false;
    -
    118 }
    -
    119
    -
    120 // if requested to digest until the end of file, set nbytes to the remaining size
    -
    121 if (nbytes == 0) {
    -
    122 nbytes = size - offset;
    -
    123 }
    -
    124
    -
    125 // move the seek pointer to the beginning of the offset
    -
    126 if (offset > 0) {
    -
    127 if (lseek(fd, offset, SEEK_SET) != offset) {
    -
    128 close(fd);
    -
    129 return false;
    -
    130 }
    -
    131 }
    -
    132
    -
    133 MD5_CTX context;
    -
    134 MD5Init(&context);
    -
    135 ssize_t toread, nread;
    -
    136 unsigned char buf[32 * 1024];
    -
    137 for (toread = nbytes; toread > 0; toread -= nread) {
    -
    138 if (toread > sizeof(buf))
    -
    139 nread = read(fd, buf, sizeof(buf));
    -
    140 else
    -
    141 nread = read(fd, buf, toread);
    -
    142 if (nread < 0)
    -
    143 break;
    -
    144 MD5Update(&context, buf, nread);
    -
    145 }
    -
    146 close(fd);
    -
    147 if (toread != 0)
    -
    148 return false;
    -
    149 MD5Final(retbuf, &context);
    -
    150
    -
    151 return true;
    -
    152}
    -
    153
    -
    154/**
    -
    155 * Get 32-bit FNV1 hash.
    -
    156 *
    -
    157 * @param data source data
    -
    158 * @param nbytes size of data
    -
    159 *
    -
    160 * @return 32-bit unsigned hash value.
    -
    161 *
    -
    162 * @code
    -
    163 * uint32_t hashval = qhashfnv1_32((void*)"hello", 5);
    -
    164 * @endcode
    -
    165 *
    -
    166 * @code
    -
    167 * Fowler/Noll/Vo hash
    -
    168 *
    -
    169 * The basis of this hash algorithm was taken from an idea sent as reviewer
    -
    170 * comments to the IEEE POSIX P1003.2 committee by:
    -
    171 *
    -
    172 * Phong Vo (http://www.research.att.com/info/kpv/)
    -
    173 * Glenn Fowler (http://www.research.att.com/~gsf/)
    -
    174 *
    -
    175 * In a subsequent ballot round:
    -
    176 *
    -
    177 * Landon Curt Noll (http://www.isthe.com/chongo/)
    -
    178 *
    -
    179 * improved on their algorithm. Some people tried this hash and found that
    -
    180 * it worked rather well. In an EMail message to Landon, they named it the
    -
    181 * ``Fowler/Noll/Vo'' or FNV hash.
    -
    182 *
    -
    183 * FNV hashes are designed to be fast while maintaining a low collision rate.
    -
    184 * The FNV speed allows one to quickly hash lots of data while maintaining
    -
    185 * a reasonable collision rate. See:
    -
    186 *
    -
    187 * http://www.isthe.com/chongo/tech/comp/fnv/index.html
    -
    188 *
    -
    189 * for more details as well as other forms of the FNV hash.
    -
    190 * @endcode
    -
    191 */
    -
    192uint32_t qhashfnv1_32(const void *data, size_t nbytes) {
    -
    193 if (data == NULL || nbytes == 0)
    -
    194 return 0;
    -
    195
    -
    196 unsigned char *dp;
    -
    197 uint32_t h = 0x811C9DC5;
    -
    198
    -
    199 for (dp = (unsigned char *) data; *dp && nbytes > 0; dp++, nbytes--) {
    -
    200#ifdef __GNUC__
    -
    201 h += (h<<1) + (h<<4) + (h<<7) + (h<<8) + (h<<24);
    -
    202#else
    -
    203 h *= 0x01000193;
    -
    204#endif
    -
    205 h ^= *dp;
    -
    206 }
    -
    207
    -
    208 return h;
    -
    209}
    -
    210
    -
    211/**
    -
    212 * Get 64-bit FNV1 hash integer.
    -
    213 *
    -
    214 * @param data source data
    -
    215 * @param nbytes size of data
    -
    216 *
    -
    217 * @return 64-bit unsigned hash value.
    -
    218 *
    -
    219 * @code
    -
    220 * uint64_t fnv64 = qhashfnv1_64((void*)"hello", 5);
    -
    221 * @endcode
    -
    222 */
    -
    223uint64_t qhashfnv1_64(const void *data, size_t nbytes) {
    -
    224 if (data == NULL || nbytes == 0)
    -
    225 return 0;
    -
    226
    -
    227 unsigned char *dp;
    -
    228 uint64_t h = 0xCBF29CE484222325ULL;
    -
    229
    -
    230 for (dp = (unsigned char *) data; *dp && nbytes > 0; dp++, nbytes--) {
    -
    231#ifdef __GNUC__
    -
    232 h += (h << 1) + (h << 4) + (h << 5) +
    -
    233 (h << 7) + (h << 8) + (h << 40);
    -
    234#else
    -
    235 h *= 0x100000001B3ULL;
    -
    236#endif
    -
    237 h ^= *dp;
    -
    238 }
    -
    239
    -
    240 return h;
    -
    241}
    -
    242
    -
    243/**
    -
    244 * Get 32-bit Murmur3 hash.
    -
    245 *
    -
    246 * @param data source data
    -
    247 * @param nbytes size of data
    -
    248 *
    -
    249 * @return 32-bit unsigned hash value.
    -
    250 *
    -
    251 * @code
    -
    252 * uint32_t hashval = qhashmurmur3_32((void*)"hello", 5);
    -
    253 * @endcode
    -
    254 *
    -
    255 * @code
    -
    256 * MurmurHash3 was created by Austin Appleby in 2008. The initial
    -
    257 * implementation was published in C++ and placed in the public.
    -
    258 * https://sites.google.com/site/murmurhash/
    -
    259 * Seungyoung Kim has ported its implementation into C language
    -
    260 * in 2012 and published it as a part of qLibc component.
    -
    261 * @endcode
    -
    262 */
    -
    263uint32_t qhashmurmur3_32(const void *data, size_t nbytes) {
    -
    264 if (data == NULL || nbytes == 0)
    -
    265 return 0;
    -
    266
    -
    267 const uint32_t c1 = 0xcc9e2d51;
    -
    268 const uint32_t c2 = 0x1b873593;
    -
    269
    -
    270 const int nblocks = nbytes / 4;
    -
    271 const uint32_t *blocks = (const uint32_t *) (data);
    -
    272 const uint8_t *tail = (const uint8_t *) (data + (nblocks * 4));
    -
    273
    -
    274 uint32_t h = 0;
    -
    275
    -
    276 int i;
    -
    277 uint32_t k;
    -
    278 for (i = 0; i < nblocks; i++) {
    -
    279 k = blocks[i];
    -
    280
    -
    281 k *= c1;
    -
    282 k = (k << 15) | (k >> (32 - 15));
    -
    283 k *= c2;
    -
    284
    -
    285 h ^= k;
    -
    286 h = (h << 13) | (h >> (32 - 13));
    -
    287 h = (h * 5) + 0xe6546b64;
    -
    288 }
    -
    289
    -
    290 k = 0;
    -
    291 switch (nbytes & 3) {
    -
    292 case 3:
    -
    293 k ^= tail[2] << 16;
    -
    294 case 2:
    -
    295 k ^= tail[1] << 8;
    -
    296 case 1:
    -
    297 k ^= tail[0];
    -
    298 k *= c1;
    -
    299 k = (k << 15) | (k >> (32 - 15));
    -
    300 k *= c2;
    -
    301 h ^= k;
    -
    302 };
    -
    303
    -
    304 h ^= nbytes;
    -
    305
    -
    306 h ^= h >> 16;
    -
    307 h *= 0x85ebca6b;
    -
    308 h ^= h >> 13;
    -
    309 h *= 0xc2b2ae35;
    -
    310 h ^= h >> 16;
    -
    311
    -
    312 return h;
    -
    313}
    -
    314
    -
    315/**
    -
    316 * Get 128-bit Murmur3 hash.
    -
    317 *
    -
    318 * @param data source data
    -
    319 * @param nbytes size of data
    -
    320 * @param retbuf user buffer. It must be at leat 16-bytes long.
    -
    321 *
    -
    322 * @return true if successful, otherwise false.
    -
    323 *
    -
    324 * @code
    -
    325 * // get 128-bit Murmur3 hash.
    -
    326 * unsigned char hash[16];
    -
    327 * qhashmurmur3_128((void*)"hello", 5, hash);
    -
    328 *
    -
    329 * // hex encode
    -
    330 * char *ascii = qhex_encode(hash, 16);
    -
    331 * printf("Hex encoded Murmur3: %s\n", ascii);
    -
    332 * free(ascii);
    -
    333 * @endcode
    -
    334 */
    -
    335bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf) {
    -
    336 if (data == NULL || nbytes == 0)
    -
    337 return false;
    -
    338
    -
    339 const uint64_t c1 = 0x87c37b91114253d5ULL;
    -
    340 const uint64_t c2 = 0x4cf5ad432745937fULL;
    -
    341
    -
    342 const int nblocks = nbytes / 16;
    -
    343 const uint64_t *blocks = (const uint64_t *) (data);
    -
    344 const uint8_t *tail = (const uint8_t *) (data + (nblocks * 16));
    -
    345
    -
    346 uint64_t h1 = 0;
    -
    347 uint64_t h2 = 0;
    -
    348
    -
    349 int i;
    -
    350 uint64_t k1, k2;
    -
    351 for (i = 0; i < nblocks; i++) {
    -
    352 k1 = blocks[i * 2 + 0];
    -
    353 k2 = blocks[i * 2 + 1];
    -
    354
    -
    355 k1 *= c1;
    -
    356 k1 = (k1 << 31) | (k1 >> (64 - 31));
    -
    357 k1 *= c2;
    -
    358 h1 ^= k1;
    -
    359
    -
    360 h1 = (h1 << 27) | (h1 >> (64 - 27));
    -
    361 h1 += h2;
    -
    362 h1 = h1 * 5 + 0x52dce729;
    -
    363
    -
    364 k2 *= c2;
    -
    365 k2 = (k2 << 33) | (k2 >> (64 - 33));
    -
    366 k2 *= c1;
    -
    367 h2 ^= k2;
    -
    368
    -
    369 h2 = (h2 << 31) | (h2 >> (64 - 31));
    -
    370 h2 += h1;
    -
    371 h2 = h2 * 5 + 0x38495ab5;
    -
    372 }
    -
    373
    -
    374 k1 = k2 = 0;
    -
    375 switch (nbytes & 15) {
    -
    376 case 15:
    -
    377 k2 ^= (uint64_t)(tail[14]) << 48;
    -
    378 case 14:
    -
    379 k2 ^= (uint64_t)(tail[13]) << 40;
    -
    380 case 13:
    -
    381 k2 ^= (uint64_t)(tail[12]) << 32;
    -
    382 case 12:
    -
    383 k2 ^= (uint64_t)(tail[11]) << 24;
    -
    384 case 11:
    -
    385 k2 ^= (uint64_t)(tail[10]) << 16;
    -
    386 case 10:
    -
    387 k2 ^= (uint64_t)(tail[9]) << 8;
    -
    388 case 9:
    -
    389 k2 ^= (uint64_t)(tail[8]) << 0;
    -
    390 k2 *= c2;
    -
    391 k2 = (k2 << 33) | (k2 >> (64 - 33));
    -
    392 k2 *= c1;
    -
    393 h2 ^= k2;
    -
    394
    -
    395 case 8:
    -
    396 k1 ^= (uint64_t)(tail[7]) << 56;
    -
    397 case 7:
    -
    398 k1 ^= (uint64_t)(tail[6]) << 48;
    -
    399 case 6:
    -
    400 k1 ^= (uint64_t)(tail[5]) << 40;
    -
    401 case 5:
    -
    402 k1 ^= (uint64_t)(tail[4]) << 32;
    -
    403 case 4:
    -
    404 k1 ^= (uint64_t)(tail[3]) << 24;
    -
    405 case 3:
    -
    406 k1 ^= (uint64_t)(tail[2]) << 16;
    -
    407 case 2:
    -
    408 k1 ^= (uint64_t)(tail[1]) << 8;
    -
    409 case 1:
    -
    410 k1 ^= (uint64_t)(tail[0]) << 0;
    -
    411 k1 *= c1;
    -
    412 k1 = (k1 << 31) | (k1 >> (64 - 31));
    -
    413 k1 *= c2;
    -
    414 h1 ^= k1;
    -
    415 };
    -
    416
    -
    417 //----------
    -
    418 // finalization
    -
    419
    -
    420 h1 ^= nbytes;
    -
    421 h2 ^= nbytes;
    -
    422
    -
    423 h1 += h2;
    -
    424 h2 += h1;
    -
    425
    -
    426 h1 ^= h1 >> 33;
    -
    427 h1 *= 0xff51afd7ed558ccdULL;
    -
    428 h1 ^= h1 >> 33;
    -
    429 h1 *= 0xc4ceb9fe1a85ec53ULL;
    -
    430 h1 ^= h1 >> 33;
    -
    431
    -
    432 h2 ^= h2 >> 33;
    -
    433 h2 *= 0xff51afd7ed558ccdULL;
    -
    434 h2 ^= h2 >> 33;
    -
    435 h2 *= 0xc4ceb9fe1a85ec53ULL;
    -
    436 h2 ^= h2 >> 33;
    -
    437
    -
    438 h1 += h2;
    -
    439 h2 += h1;
    -
    440
    -
    441 ((uint64_t *) retbuf)[0] = h1;
    -
    442 ((uint64_t *) retbuf)[1] = h2;
    -
    443
    -
    444 return true;
    -
    445}
    -
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition qhash.c:67
    -
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition qhash.c:263
    -
    uint64_t qhashfnv1_64(const void *data, size_t nbytes)
    Get 64-bit FNV1 hash integer.
    Definition qhash.c:223
    -
    bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf)
    Get 128-bit Murmur3 hash.
    Definition qhash.c:335
    -
    uint32_t qhashfnv1_32(const void *data, size_t nbytes)
    Get 32-bit FNV1 hash.
    Definition qhash.c:192
    -
    bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes, void *retbuf)
    Get 128-bit MD5 hash of a file contents.
    Definition qhash.c:97
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qhash.c Hash APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <stdint.h>
    +
    37 #include <string.h>
    +
    38 #include <unistd.h>
    +
    39 #include <fcntl.h>
    +
    40 #include <errno.h>
    +
    41 #include <sys/types.h>
    +
    42 #include <sys/stat.h>
    +
    43 #include "md5/md5.h"
    +
    44 #include "qinternal.h"
    +
    45 #include "utilities/qhash.h"
    +
    46 
    +
    47 /**
    +
    48  * Calculate 128-bit(16-bytes) MD5 hash.
    +
    49  *
    +
    50  * @param data source object
    +
    51  * @param nbytes size of data
    +
    52  * @param retbuf user buffer. It must be at leat 16-bytes long.
    +
    53  *
    +
    54  * @return true if successful, otherwise false.
    +
    55  *
    +
    56  * @code
    +
    57  * // get MD5
    +
    58  * unsigned char md5hash[16];
    +
    59  * qhashmd5((void*)"hello", 5, md5hash);
    +
    60  *
    +
    61  * // hex encode
    +
    62  * char *md5ascii = qhex_encode(md5hash, 16);
    +
    63  * printf("Hex encoded MD5: %s\n", md5ascii);
    +
    64  * free(md5ascii);
    +
    65  * @endcode
    +
    66  */
    +
    67 bool qhashmd5(const void *data, size_t nbytes, void *retbuf) {
    +
    68  if (data == NULL || retbuf == NULL) {
    +
    69  errno = EINVAL;
    +
    70  return false;
    +
    71  }
    +
    72 
    +
    73  MD5_CTX context;
    +
    74  MD5Init(&context);
    +
    75  MD5Update(&context, (unsigned char *) data, (unsigned int) nbytes);
    +
    76  MD5Final(retbuf, &context);
    +
    77 
    +
    78  return true;
    +
    79 }
    +
    80 
    +
    81 /**
    +
    82  * Get 128-bit MD5 hash of a file contents.
    +
    83  *
    +
    84  * @param filepath file path
    +
    85  * @param offset start offset. Set to 0 to digest from beginning of file.
    +
    86  * @param nbytes number of bytes to digest. Set to 0 to digest until end
    +
    87  * of file.
    +
    88  * @param retbuf user buffer. It must be at leat 16-bytes long.
    +
    89  *
    +
    90  * @return true if successful, otherwise false.
    +
    91  *
    +
    92  * @code
    +
    93  * unsigned char md5hash[16];
    +
    94  * qhashmd5_file("/tmp/test.dat", 0, 0, md5hash);
    +
    95  * @endcode
    +
    96  */
    +
    97 bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes,
    +
    98  void *retbuf) {
    +
    99  if (filepath == NULL || offset < 0 || nbytes < 0 || retbuf == NULL) {
    +
    100  errno = EINVAL;
    +
    101  return false;
    +
    102  }
    +
    103 
    +
    104  int fd = open(filepath, O_RDONLY, 0);
    +
    105  if (fd < 0)
    +
    106  return false;
    +
    107 
    +
    108  struct stat st;
    +
    109  if (fstat(fd, &st) < 0)
    +
    110  return false;
    +
    111  size_t size = st.st_size;
    +
    112 
    +
    113  // check filesize
    +
    114  if (size < offset + nbytes) {
    +
    115  errno = EINVAL;
    +
    116  close(fd);
    +
    117  return false;
    +
    118  }
    +
    119 
    +
    120  // if requested to digest until the end of file, set nbytes to the remaining size
    +
    121  if (nbytes == 0) {
    +
    122  nbytes = size - offset;
    +
    123  }
    +
    124 
    +
    125  // move the seek pointer to the beginning of the offset
    +
    126  if (offset > 0) {
    +
    127  if (lseek(fd, offset, SEEK_SET) != offset) {
    +
    128  close(fd);
    +
    129  return false;
    +
    130  }
    +
    131  }
    +
    132 
    +
    133  MD5_CTX context;
    +
    134  MD5Init(&context);
    +
    135  ssize_t toread, nread;
    +
    136  unsigned char buf[32 * 1024];
    +
    137  for (toread = nbytes; toread > 0; toread -= nread) {
    +
    138  if (toread > sizeof(buf))
    +
    139  nread = read(fd, buf, sizeof(buf));
    +
    140  else
    +
    141  nread = read(fd, buf, toread);
    +
    142  if (nread < 0)
    +
    143  break;
    +
    144  MD5Update(&context, buf, nread);
    +
    145  }
    +
    146  close(fd);
    +
    147  if (toread != 0)
    +
    148  return false;
    +
    149  MD5Final(retbuf, &context);
    +
    150 
    +
    151  return true;
    +
    152 }
    +
    153 
    +
    154 /**
    +
    155  * Get 32-bit FNV1 hash.
    +
    156  *
    +
    157  * @param data source data
    +
    158  * @param nbytes size of data
    +
    159  *
    +
    160  * @return 32-bit unsigned hash value.
    +
    161  *
    +
    162  * @code
    +
    163  * uint32_t hashval = qhashfnv1_32((void*)"hello", 5);
    +
    164  * @endcode
    +
    165  *
    +
    166  * @code
    +
    167  * Fowler/Noll/Vo hash
    +
    168  *
    +
    169  * The basis of this hash algorithm was taken from an idea sent as reviewer
    +
    170  * comments to the IEEE POSIX P1003.2 committee by:
    +
    171  *
    +
    172  * Phong Vo (http://www.research.att.com/info/kpv/)
    +
    173  * Glenn Fowler (http://www.research.att.com/~gsf/)
    +
    174  *
    +
    175  * In a subsequent ballot round:
    +
    176  *
    +
    177  * Landon Curt Noll (http://www.isthe.com/chongo/)
    +
    178  *
    +
    179  * improved on their algorithm. Some people tried this hash and found that
    +
    180  * it worked rather well. In an EMail message to Landon, they named it the
    +
    181  * ``Fowler/Noll/Vo'' or FNV hash.
    +
    182  *
    +
    183  * FNV hashes are designed to be fast while maintaining a low collision rate.
    +
    184  * The FNV speed allows one to quickly hash lots of data while maintaining
    +
    185  * a reasonable collision rate. See:
    +
    186  *
    +
    187  * http://www.isthe.com/chongo/tech/comp/fnv/index.html
    +
    188  *
    +
    189  * for more details as well as other forms of the FNV hash.
    +
    190  * @endcode
    +
    191  */
    +
    192 uint32_t qhashfnv1_32(const void *data, size_t nbytes) {
    +
    193  if (data == NULL || nbytes == 0)
    +
    194  return 0;
    +
    195 
    +
    196  unsigned char *dp;
    +
    197  uint32_t h = 0x811C9DC5;
    +
    198 
    +
    199  for (dp = (unsigned char *) data; *dp && nbytes > 0; dp++, nbytes--) {
    +
    200 #ifdef __GNUC__
    +
    201  h += (h<<1) + (h<<4) + (h<<7) + (h<<8) + (h<<24);
    +
    202 #else
    +
    203  h *= 0x01000193;
    +
    204 #endif
    +
    205  h ^= *dp;
    +
    206  }
    +
    207 
    +
    208  return h;
    +
    209 }
    +
    210 
    +
    211 /**
    +
    212  * Get 64-bit FNV1 hash integer.
    +
    213  *
    +
    214  * @param data source data
    +
    215  * @param nbytes size of data
    +
    216  *
    +
    217  * @return 64-bit unsigned hash value.
    +
    218  *
    +
    219  * @code
    +
    220  * uint64_t fnv64 = qhashfnv1_64((void*)"hello", 5);
    +
    221  * @endcode
    +
    222  */
    +
    223 uint64_t qhashfnv1_64(const void *data, size_t nbytes) {
    +
    224  if (data == NULL || nbytes == 0)
    +
    225  return 0;
    +
    226 
    +
    227  unsigned char *dp;
    +
    228  uint64_t h = 0xCBF29CE484222325ULL;
    +
    229 
    +
    230  for (dp = (unsigned char *) data; *dp && nbytes > 0; dp++, nbytes--) {
    +
    231 #ifdef __GNUC__
    +
    232  h += (h << 1) + (h << 4) + (h << 5) +
    +
    233  (h << 7) + (h << 8) + (h << 40);
    +
    234 #else
    +
    235  h *= 0x100000001B3ULL;
    +
    236 #endif
    +
    237  h ^= *dp;
    +
    238  }
    +
    239 
    +
    240  return h;
    +
    241 }
    +
    242 
    +
    243 /**
    +
    244  * Get 32-bit Murmur3 hash.
    +
    245  *
    +
    246  * @param data source data
    +
    247  * @param nbytes size of data
    +
    248  *
    +
    249  * @return 32-bit unsigned hash value.
    +
    250  *
    +
    251  * @code
    +
    252  * uint32_t hashval = qhashmurmur3_32((void*)"hello", 5);
    +
    253  * @endcode
    +
    254  *
    +
    255  * @code
    +
    256  * MurmurHash3 was created by Austin Appleby in 2008. The initial
    +
    257  * implementation was published in C++ and placed in the public.
    +
    258  * https://sites.google.com/site/murmurhash/
    +
    259  * Seungyoung Kim has ported its implementation into C language
    +
    260  * in 2012 and published it as a part of qLibc component.
    +
    261  * @endcode
    +
    262  */
    +
    263 uint32_t qhashmurmur3_32(const void *data, size_t nbytes) {
    +
    264  if (data == NULL || nbytes == 0)
    +
    265  return 0;
    +
    266 
    +
    267  const uint32_t c1 = 0xcc9e2d51;
    +
    268  const uint32_t c2 = 0x1b873593;
    +
    269 
    +
    270  const int nblocks = nbytes / 4;
    +
    271  const uint32_t *blocks = (const uint32_t *) (data);
    +
    272  const uint8_t *tail = (const uint8_t *) (data + (nblocks * 4));
    +
    273 
    +
    274  uint32_t h = 0;
    +
    275 
    +
    276  int i;
    +
    277  uint32_t k;
    +
    278  for (i = 0; i < nblocks; i++) {
    +
    279  k = blocks[i];
    +
    280 
    +
    281  k *= c1;
    +
    282  k = (k << 15) | (k >> (32 - 15));
    +
    283  k *= c2;
    +
    284 
    +
    285  h ^= k;
    +
    286  h = (h << 13) | (h >> (32 - 13));
    +
    287  h = (h * 5) + 0xe6546b64;
    +
    288  }
    +
    289 
    +
    290  k = 0;
    +
    291  switch (nbytes & 3) {
    +
    292  case 3:
    +
    293  k ^= tail[2] << 16;
    +
    294  case 2:
    +
    295  k ^= tail[1] << 8;
    +
    296  case 1:
    +
    297  k ^= tail[0];
    +
    298  k *= c1;
    +
    299  k = (k << 15) | (k >> (32 - 15));
    +
    300  k *= c2;
    +
    301  h ^= k;
    +
    302  };
    +
    303 
    +
    304  h ^= nbytes;
    +
    305 
    +
    306  h ^= h >> 16;
    +
    307  h *= 0x85ebca6b;
    +
    308  h ^= h >> 13;
    +
    309  h *= 0xc2b2ae35;
    +
    310  h ^= h >> 16;
    +
    311 
    +
    312  return h;
    +
    313 }
    +
    314 
    +
    315 /**
    +
    316  * Get 128-bit Murmur3 hash.
    +
    317  *
    +
    318  * @param data source data
    +
    319  * @param nbytes size of data
    +
    320  * @param retbuf user buffer. It must be at leat 16-bytes long.
    +
    321  *
    +
    322  * @return true if successful, otherwise false.
    +
    323  *
    +
    324  * @code
    +
    325  * // get 128-bit Murmur3 hash.
    +
    326  * unsigned char hash[16];
    +
    327  * qhashmurmur3_128((void*)"hello", 5, hash);
    +
    328  *
    +
    329  * // hex encode
    +
    330  * char *ascii = qhex_encode(hash, 16);
    +
    331  * printf("Hex encoded Murmur3: %s\n", ascii);
    +
    332  * free(ascii);
    +
    333  * @endcode
    +
    334  */
    +
    335 bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf) {
    +
    336  if (data == NULL || nbytes == 0)
    +
    337  return false;
    +
    338 
    +
    339  const uint64_t c1 = 0x87c37b91114253d5ULL;
    +
    340  const uint64_t c2 = 0x4cf5ad432745937fULL;
    +
    341 
    +
    342  const int nblocks = nbytes / 16;
    +
    343  const uint64_t *blocks = (const uint64_t *) (data);
    +
    344  const uint8_t *tail = (const uint8_t *) (data + (nblocks * 16));
    +
    345 
    +
    346  uint64_t h1 = 0;
    +
    347  uint64_t h2 = 0;
    +
    348 
    +
    349  int i;
    +
    350  uint64_t k1, k2;
    +
    351  for (i = 0; i < nblocks; i++) {
    +
    352  k1 = blocks[i * 2 + 0];
    +
    353  k2 = blocks[i * 2 + 1];
    +
    354 
    +
    355  k1 *= c1;
    +
    356  k1 = (k1 << 31) | (k1 >> (64 - 31));
    +
    357  k1 *= c2;
    +
    358  h1 ^= k1;
    +
    359 
    +
    360  h1 = (h1 << 27) | (h1 >> (64 - 27));
    +
    361  h1 += h2;
    +
    362  h1 = h1 * 5 + 0x52dce729;
    +
    363 
    +
    364  k2 *= c2;
    +
    365  k2 = (k2 << 33) | (k2 >> (64 - 33));
    +
    366  k2 *= c1;
    +
    367  h2 ^= k2;
    +
    368 
    +
    369  h2 = (h2 << 31) | (h2 >> (64 - 31));
    +
    370  h2 += h1;
    +
    371  h2 = h2 * 5 + 0x38495ab5;
    +
    372  }
    +
    373 
    +
    374  k1 = k2 = 0;
    +
    375  switch (nbytes & 15) {
    +
    376  case 15:
    +
    377  k2 ^= (uint64_t)(tail[14]) << 48;
    +
    378  case 14:
    +
    379  k2 ^= (uint64_t)(tail[13]) << 40;
    +
    380  case 13:
    +
    381  k2 ^= (uint64_t)(tail[12]) << 32;
    +
    382  case 12:
    +
    383  k2 ^= (uint64_t)(tail[11]) << 24;
    +
    384  case 11:
    +
    385  k2 ^= (uint64_t)(tail[10]) << 16;
    +
    386  case 10:
    +
    387  k2 ^= (uint64_t)(tail[9]) << 8;
    +
    388  case 9:
    +
    389  k2 ^= (uint64_t)(tail[8]) << 0;
    +
    390  k2 *= c2;
    +
    391  k2 = (k2 << 33) | (k2 >> (64 - 33));
    +
    392  k2 *= c1;
    +
    393  h2 ^= k2;
    +
    394 
    +
    395  case 8:
    +
    396  k1 ^= (uint64_t)(tail[7]) << 56;
    +
    397  case 7:
    +
    398  k1 ^= (uint64_t)(tail[6]) << 48;
    +
    399  case 6:
    +
    400  k1 ^= (uint64_t)(tail[5]) << 40;
    +
    401  case 5:
    +
    402  k1 ^= (uint64_t)(tail[4]) << 32;
    +
    403  case 4:
    +
    404  k1 ^= (uint64_t)(tail[3]) << 24;
    +
    405  case 3:
    +
    406  k1 ^= (uint64_t)(tail[2]) << 16;
    +
    407  case 2:
    +
    408  k1 ^= (uint64_t)(tail[1]) << 8;
    +
    409  case 1:
    +
    410  k1 ^= (uint64_t)(tail[0]) << 0;
    +
    411  k1 *= c1;
    +
    412  k1 = (k1 << 31) | (k1 >> (64 - 31));
    +
    413  k1 *= c2;
    +
    414  h1 ^= k1;
    +
    415  };
    +
    416 
    +
    417  //----------
    +
    418  // finalization
    +
    419 
    +
    420  h1 ^= nbytes;
    +
    421  h2 ^= nbytes;
    +
    422 
    +
    423  h1 += h2;
    +
    424  h2 += h1;
    +
    425 
    +
    426  h1 ^= h1 >> 33;
    +
    427  h1 *= 0xff51afd7ed558ccdULL;
    +
    428  h1 ^= h1 >> 33;
    +
    429  h1 *= 0xc4ceb9fe1a85ec53ULL;
    +
    430  h1 ^= h1 >> 33;
    +
    431 
    +
    432  h2 ^= h2 >> 33;
    +
    433  h2 *= 0xff51afd7ed558ccdULL;
    +
    434  h2 ^= h2 >> 33;
    +
    435  h2 *= 0xc4ceb9fe1a85ec53ULL;
    +
    436  h2 ^= h2 >> 33;
    +
    437 
    +
    438  h1 += h2;
    +
    439  h2 += h1;
    +
    440 
    +
    441  ((uint64_t *) retbuf)[0] = h1;
    +
    442  ((uint64_t *) retbuf)[1] = h2;
    +
    443 
    +
    444  return true;
    +
    445 }
    +
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition: qhash.c:67
    +
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition: qhash.c:263
    +
    uint64_t qhashfnv1_64(const void *data, size_t nbytes)
    Get 64-bit FNV1 hash integer.
    Definition: qhash.c:223
    +
    bool qhashmurmur3_128(const void *data, size_t nbytes, void *retbuf)
    Get 128-bit Murmur3 hash.
    Definition: qhash.c:335
    +
    uint32_t qhashfnv1_32(const void *data, size_t nbytes)
    Get 32-bit FNV1 hash.
    Definition: qhash.c:192
    +
    bool qhashmd5_file(const char *filepath, off_t offset, ssize_t nbytes, void *retbuf)
    Get 128-bit MD5 hash of a file contents.
    Definition: qhash.c:97
    diff --git a/doc/html/qhasharr_8c.html b/doc/html/qhasharr_8c.html index 87aa45ec..d935b825 100644 --- a/doc/html/qhasharr_8c.html +++ b/doc/html/qhasharr_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qhasharr.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qhasharr.c File Reference
    +
    +
    qhasharr.c File Reference

    @@ -71,70 +71,72 @@

    Go to the source code of this file.

    - - + - +

    +

    Macros

    #define COLLISION_MARK   (-1)
    +#define COLLISION_MARK   (-1)
     
    #define EXTBLOCK_MARK   (-2)
    +#define EXTBLOCK_MARK   (-2)
     
    - - + - - - + + + - + - + - + - + - - - - - - - - - + + + + + + + + + - + - + - + - + - + - + - + - +

    +

    Functions

    size_t qhasharr_calculate_memsize (int max)
     Get how much memory is needed for N slots.
     Get how much memory is needed for N slots. More...
     
    qhasharr_t * qhasharr (void *memory, size_t memsize)
     Initialize static hash table.
     
    qhasharr_t * qhasharr (void *memory, size_t memsize)
     Initialize static hash table. More...
     
    bool qhasharr_put (qhasharr_t *tbl, const char *name, const void *data, size_t datasize)
     qhasharr->put(): Put an object into this table.
     qhasharr->put(): Put an object into this table. More...
     
    bool qhasharr_putstr (qhasharr_t *tbl, const char *name, const char *data)
     qhasharr->putstr(): Put a string into this table
     qhasharr->putstr(): Put a string into this table More...
     
    bool qhasharr_putstrf (qhasharr_t *tbl, const char *name, const char *format,...)
     qhasharr->putstrf(): Put a formatted string into this table.
     qhasharr->putstrf(): Put a formatted string into this table. More...
     
    bool qhasharr_put_by_obj (qhasharr_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
     qhasharr->put_by_obj(): ut an object into this table by key object.
     qhasharr->put_by_obj(): ut an object into this table by key object. More...
     
    void * qhasharr_get (qhasharr_t *tbl, const char *name, size_t *datasize)
     qhasharr->get(): Get an object from this table
     
    char * qhasharr_getstr (qhasharr_t *tbl, const char *name)
     qhasharr->getstr(): Finds an object with given name and returns as string type.
     
    void * qhasharr_get_by_obj (qhasharr_t *tbl, const void *name, size_t namesize, size_t *datasize)
     qhasharr->get_by_object(): Get an object from this table by key object
     
    void * qhasharr_get (qhasharr_t *tbl, const char *name, size_t *datasize)
     qhasharr->get(): Get an object from this table More...
     
    char * qhasharr_getstr (qhasharr_t *tbl, const char *name)
     qhasharr->getstr(): Finds an object with given name and returns as string type. More...
     
    void * qhasharr_get_by_obj (qhasharr_t *tbl, const void *name, size_t namesize, size_t *datasize)
     qhasharr->get_by_object(): Get an object from this table by key object More...
     
    bool qhasharr_remove (qhasharr_t *tbl, const char *name)
     qhasharr->remove(): Remove an object from this table.
     qhasharr->remove(): Remove an object from this table. More...
     
    bool qhasharr_remove_by_obj (qhasharr_t *tbl, const char *name, size_t namesize)
     qhasharr->remove_by_obj(): Remove an object from this table by key object
     qhasharr->remove_by_obj(): Remove an object from this table by key object More...
     
    bool qhasharr_remove_by_idx (qhasharr_t *tbl, int idx)
     qhasharr->remove_by_idx(): Remove an object from this table by index number.
     qhasharr->remove_by_idx(): Remove an object from this table by index number. More...
     
    bool qhasharr_getnext (qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx)
     qhasharr->getnext(): Get next element.
     qhasharr->getnext(): Get next element. More...
     
    int qhasharr_size (qhasharr_t *tbl, int *maxslots, int *usedslots)
     qhasharr->size(): Returns the number of objects in this table.
     qhasharr->size(): Returns the number of objects in this table. More...
     
    void qhasharr_clear (qhasharr_t *tbl)
     qhasharr->clear(): Clears this table so that it contains no keys.
     qhasharr->clear(): Clears this table so that it contains no keys. More...
     
    bool qhasharr_debug (qhasharr_t *tbl, FILE *out)
     qhasharr->debug(): Print hash table for debugging purpose
     qhasharr->debug(): Print hash table for debugging purpose More...
     
    void qhasharr_free (qhasharr_t *tbl)
     qhasharr->free(): De-allocate table reference object.
     qhasharr->free(): De-allocate table reference object. More...
     

    Detailed Description

    Static(array) hash-table implementation.

    -

    qhasharr implements a hash-table which maps keys to values and stores into fixed size static memory like shared-memory and memory-mapped file. The creator qhasharr() initializes static memory to makes small slots in it. The default slot size factors are defined in Q_HASHARR_NAMESIZE and Q_HASHARR_DATASIZE. And they are applied at compile time.

    +

    qhasharr implements a hash-table which maps keys to values and stores into fixed size static memory like shared-memory and memory-mapped file. The creator qhasharr() initializes static memory to makes small slots in it. The default slot size factors are defined in Q_HASHARR_NAMESIZE and Q_HASHARR_DATASIZE. And they are applied at compile time.

    The value part of an element will be stored across several slots if it's size exceeds the slot size. But the key part of an element will be truncated if the size exceeds and it's length and more complex MD5 hash value will be stored with the key. So to look up a particular key, first we find an element which has same hash value. If the key was not truncated, we just do key comparison. But if the key was truncated because it's length exceeds, we do both md5 and key comparison(only stored size) to verify that the key is same. So please be aware of that, theoretically there is a possibility we pick wrong element in case a key exceeds the limit, has same length and MD5 hash with lookup key. But this possibility is very low and almost zero in practice.

    qhasharr hash-table does not provide thread-safe handling intentionally and let users determine whether to provide locking mechanism or not, depending on the use cases. When there's race conditions expected, you should provide a shared resource control using mutex or semaphore to make sure data gets updated by one instance at a time.

    [Data Structure Diagram]
    @@ -154,7 +156,7 @@
    +--------------------------------------------------------------------------+
    // initialize hash-table.
    char memory[1000 * 10];
    -
    qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    +
    qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    if(tbl == NULL) return;
    // insert elements (key duplication does not allowed)
    @@ -173,19 +175,19 @@
    // Release reference object.
    tbl->free(tbl);
    -
    qhasharr_t * qhasharr(void *memory, size_t memsize)
    Initialize static hash table.
    Definition qhasharr.c:208
    +
    qhasharr_t * qhasharr(void *memory, size_t memsize)
    Initialize static hash table.
    Definition: qhasharr.c:208

    An example for using hash table over shared memory.

    [CREATOR SIDE]
    int maxslots = 1000;
    -
    int memsize = qhasharr_calculate_memsize(maxslots);
    +
    int memsize = qhasharr_calculate_memsize(maxslots);
    // create shared memory
    -
    int shmid = qshm_init("/tmp/some_id_file", 'q', memsize, true);
    +
    int shmid = qshm_init("/tmp/some_id_file", 'q', memsize, true);
    if(shmid < 0) return -1; // creation failed
    -
    void *memory = qshm_get(shmid);
    +
    void *memory = qshm_get(shmid);
    // initialize hash-table
    -
    qhasharr_t *tbl = qhasharr(memory, memsize);
    +
    qhasharr_t *tbl = qhasharr(memory, memsize);
    if(hasharr == NULL) return -1;
    (...your codes with your own locking mechanism...)
    @@ -194,64 +196,31 @@
    tbl->free(tbl);
    // destroy shared memory
    -
    qshm_free(shmid);
    +
    qshm_free(shmid);
    [USER SIDE]
    -
    int shmid = qshm_getid("/tmp/some_id_file", 'q');
    +
    int shmid = qshm_getid("/tmp/some_id_file", 'q');
    // get shared memory
    -
    void *memory = qshm_get(shmid);
    +
    void *memory = qshm_get(shmid);
    // map existing memory into table
    -
    qhasharr_t *tbl = qhasharr(memory, 0);
    +
    qhasharr_t *tbl = qhasharr(memory, 0);
    (...your codes with your own locking mechanism...)
    // Release reference object
    tbl->free(tbl);
    -
    size_t qhasharr_calculate_memsize(int max)
    Get how much memory is needed for N slots.
    Definition qhasharr.c:179
    -
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition qshm.c:149
    -
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition qshm.c:91
    -
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition qshm.c:127
    -
    bool qshm_free(int shmid)
    De-allocate shared memory.
    Definition qshm.c:167
    +
    size_t qhasharr_calculate_memsize(int max)
    Get how much memory is needed for N slots.
    Definition: qhasharr.c:179
    +
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition: qshm.c:149
    +
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition: qshm.c:91
    +
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition: qshm.c:127
    +
    bool qshm_free(int shmid)
    De-allocate shared memory.
    Definition: qshm.c:167

    Definition in file qhasharr.c.

    -

    Macro Definition Documentation

    - -

    ◆ COLLISION_MARK

    - -
    -
    - - - - -
    #define COLLISION_MARK   (-1)
    -
    - -

    Definition at line 150 of file qhasharr.c.

    - -
    -
    - -

    ◆ EXTBLOCK_MARK

    - -
    -
    - - - - -
    #define EXTBLOCK_MARK   (-2)
    -
    - -

    Definition at line 151 of file qhasharr.c.

    - -
    -
    -

    Function Documentation

    - -

    ◆ qhasharr_calculate_memsize()

    +

    Function Documentation

    + +

    ◆ qhasharr_calculate_memsize()

    @@ -280,14 +249,14 @@

    -

    ◆ qhasharr()

    + +

    ◆ qhasharr()

    - + @@ -329,17 +298,17 @@

    char memory[112 * 100];
    // Initialize new table.
    -
    qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    +
    qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    // Use existing table.
    -
    qhasharr_t *tbl2 = qhasharr(memory, 0);
    +
    qhasharr_t *tbl2 = qhasharr(memory, 0);

    Definition at line 208 of file qhasharr.c.

    - -

    ◆ qhasharr_put()

    + +

    ◆ qhasharr_put()

    @@ -403,8 +372,8 @@

    -

    ◆ qhasharr_putstr()

    + +

    ◆ qhasharr_putstr()

    @@ -461,8 +430,8 @@

    -

    ◆ qhasharr_putstrf()

    + +

    ◆ qhasharr_putstrf()

    @@ -526,8 +495,8 @@

    -

    ◆ qhasharr_put_by_obj()

    + +

    ◆ qhasharr_put_by_obj()

    @@ -598,14 +567,14 @@

    -

    ◆ qhasharr_get()

    + +

    ◆ qhasharr_get()

    qhasharr_t * qhasharr qhasharr_t* qhasharr ( void *  memory,
    - + @@ -657,14 +626,14 @@

    -

    ◆ qhasharr_getstr()

    + +

    ◆ qhasharr_getstr()

    void * qhasharr_get void* qhasharr_get ( qhasharr_t *  tbl,
    - + @@ -709,14 +678,14 @@

    -

    ◆ qhasharr_get_by_obj()

    + +

    ◆ qhasharr_get_by_obj()

    char * qhasharr_getstr char* qhasharr_getstr ( qhasharr_t *  tbl,
    - + @@ -775,8 +744,8 @@

    -

    ◆ qhasharr_remove()

    + +

    ◆ qhasharr_remove()

    @@ -826,8 +795,8 @@

    -

    ◆ qhasharr_remove_by_obj()

    + +

    ◆ qhasharr_remove_by_obj()

    @@ -884,8 +853,8 @@

    -

    ◆ qhasharr_remove_by_idx()

    + +

    ◆ qhasharr_remove_by_idx()

    @@ -946,8 +915,8 @@

    -

    ◆ qhasharr_getnext()

    + +

    ◆ qhasharr_getnext()

    @@ -1012,8 +981,8 @@

    -

    ◆ qhasharr_size()

    + +

    ◆ qhasharr_size()

    @@ -1059,8 +1028,8 @@

    -

    ◆ qhasharr_clear()

    + +

    ◆ qhasharr_clear()

    @@ -1088,8 +1057,8 @@

    -

    ◆ qhasharr_debug()

    + +

    ◆ qhasharr_debug()

    @@ -1137,8 +1106,8 @@

    -

    ◆ qhasharr_free()

    + +

    ◆ qhasharr_free()

    @@ -1172,7 +1141,7 @@

    diff --git a/doc/html/qhasharr_8c.js b/doc/html/qhasharr_8c.js index 23a8b412..9bed408c 100644 --- a/doc/html/qhasharr_8c.js +++ b/doc/html/qhasharr_8c.js @@ -1,14 +1,16 @@ var qhasharr_8c = [ + [ "COLLISION_MARK", "qhasharr_8c.html#af6ff8340308b20e6f2e5c2f024313c5d", null ], + [ "EXTBLOCK_MARK", "qhasharr_8c.html#ab725ad48f24a45370685c02f0005a78d", null ], [ "qhasharr_calculate_memsize", "qhasharr_8c.html#a83012ec9fea85ca5770529997306334d", null ], - [ "qhasharr", "qhasharr_8c.html#a6f5f9ed05bab0600c251949db75f76f1", null ], + [ "qhasharr", "qhasharr_8c.html#aa0f33578b50024a4a239472c52eee18c", null ], [ "qhasharr_put", "qhasharr_8c.html#a6e43029429b7aa43a66d2b55ce3bac81", null ], [ "qhasharr_putstr", "qhasharr_8c.html#aeadc9cda7a3e29e81a1f11b914c34390", null ], [ "qhasharr_putstrf", "qhasharr_8c.html#a9df103f6016cd3d3267cdf42893ba809", null ], [ "qhasharr_put_by_obj", "qhasharr_8c.html#a3bc56610cce2c4f7ee2373ae4a40143b", null ], - [ "qhasharr_get", "qhasharr_8c.html#a63f33bbeb824803c310e672f2ceb8c70", null ], - [ "qhasharr_getstr", "qhasharr_8c.html#a875e900cd73f72bcb613842ddf5b9c3a", null ], - [ "qhasharr_get_by_obj", "qhasharr_8c.html#a9334082a845acb3edc1f62d337266acd", null ], + [ "qhasharr_get", "qhasharr_8c.html#a5cd5ed2467b044e52699c5b14fdda7c2", null ], + [ "qhasharr_getstr", "qhasharr_8c.html#a8434b123a29f0b6da9b430700ce2b728", null ], + [ "qhasharr_get_by_obj", "qhasharr_8c.html#a30a8198420daadc7290a1bf3f82468ac", null ], [ "qhasharr_remove", "qhasharr_8c.html#aac1f0f336ac79a12590a7f0993e09997", null ], [ "qhasharr_remove_by_obj", "qhasharr_8c.html#af57be2bc8caf66f6bf94cd21370ef44f", null ], [ "qhasharr_remove_by_idx", "qhasharr_8c.html#a54fa504cce007a18fa510be544307cab", null ], diff --git a/doc/html/qhasharr_8c_source.html b/doc/html/qhasharr_8c_source.html index 33e5baf6..8d3d97a4 100644 --- a/doc/html/qhasharr_8c_source.html +++ b/doc/html/qhasharr_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qhasharr.c Source File @@ -20,8 +20,8 @@

    void * qhasharr_get_by_obj void* qhasharr_get_by_obj ( qhasharr_t *  tbl,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1148 +52,1149 @@

    -
    qhasharr.c
    +
    +
    qhasharr.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qhasharr.c Static(array) hash-table implementation.
    -
    31 *
    -
    32 * qhasharr implements a hash-table which maps keys to values and stores into
    -
    33 * fixed size static memory like shared-memory and memory-mapped file.
    -
    34 * The creator qhasharr() initializes static memory to makes small slots in it.
    -
    35 * The default slot size factors are defined in Q_HASHARR_NAMESIZE and
    -
    36 * Q_HASHARR_DATASIZE. And they are applied at compile time.
    -
    37 *
    -
    38 * The value part of an element will be stored across several slots if it's size
    -
    39 * exceeds the slot size. But the key part of an element will be truncated if
    -
    40 * the size exceeds and it's length and more complex MD5 hash value will be
    -
    41 * stored with the key. So to look up a particular key, first we find an element
    -
    42 * which has same hash value. If the key was not truncated, we just do key
    -
    43 * comparison. But if the key was truncated because it's length exceeds, we do
    -
    44 * both md5 and key comparison(only stored size) to verify that the key is same.
    -
    45 * So please be aware of that, theoretically there is a possibility we pick
    -
    46 * wrong element in case a key exceeds the limit, has same length and MD5 hash
    -
    47 * with lookup key. But this possibility is very low and almost zero in practice.
    -
    48 *
    -
    49 * qhasharr hash-table does not provide thread-safe handling intentionally and
    -
    50 * let users determine whether to provide locking mechanism or not, depending on
    -
    51 * the use cases. When there's race conditions expected, you should provide a
    -
    52 * shared resource control using mutex or semaphore to make sure data gets
    -
    53 * updated by one instance at a time.
    -
    54 *
    -
    55 * @code
    -
    56 * [Data Structure Diagram]
    -
    57 *
    -
    58 * +--[Static Flat Memory Area]-----------------------------------------------+
    -
    59 * | +-[Header]---------+ +-[Slot 0]---+ +-[Slot 1]---+ +-[Slot N]---+ |
    -
    60 * | |Private table data| |KEY A|DATA A| |KEY B|DATA B| .... |KEY N|DATA N| |
    -
    61 * | +------------------+ +------------+ +------------+ +------------+ |
    -
    62 * +--------------------------------------------------------------------------+
    -
    63 *
    -
    64 * Below diagram shows how a big value is stored.
    -
    65 * +--[Static Flat Memory Area------------------------------------------------+
    -
    66 * | +--------+ +-[Slot 0]---+ +-[Slot 1]---+ +-[Slot 2]---+ +-[Slot 3]-----+ |
    -
    67 * | |TBL INFO| |KEY A|DATA A| |DATA A cont.| |KEY B|DATA B| |DATA A cont. | |
    -
    68 * | +--------+ +------------+ +------------+ +------------+ +--------------+ |
    -
    69 * | ^~~link~~^ ^~~~~~~~~~link~~~~~~~~~^ |
    -
    70 * +--------------------------------------------------------------------------+
    -
    71 * @endcode
    -
    72 *
    -
    73 * @code
    -
    74 * // initialize hash-table.
    -
    75 * char memory[1000 * 10];
    -
    76 * qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    -
    77 * if(tbl == NULL) return;
    -
    78 *
    -
    79 * // insert elements (key duplication does not allowed)
    -
    80 * tbl->putstr(tbl, "e1", "a");
    -
    81 * tbl->putstr(tbl, "e2", "b");
    -
    82 * tbl->putstr(tbl, "e3", "c");
    -
    83 *
    -
    84 * // debug print out
    -
    85 * tbl->debug(tbl, stdout);
    -
    86 *
    -
    87 * char *e2 = tbl->getstr(tbl, "e2");
    -
    88 * if(e2 != NULL) {
    -
    89 * printf("getstr('e2') : %s\n", e2);
    -
    90 * free(e2);
    -
    91 * }
    -
    92 *
    -
    93 * // Release reference object.
    -
    94 * tbl->free(tbl);
    -
    95 * @endcode
    -
    96 *
    -
    97 * An example for using hash table over shared memory.
    -
    98 *
    -
    99 * @code
    -
    100 * [CREATOR SIDE]
    -
    101 * int maxslots = 1000;
    -
    102 * int memsize = qhasharr_calculate_memsize(maxslots);
    -
    103 *
    -
    104 * // create shared memory
    -
    105 * int shmid = qshm_init("/tmp/some_id_file", 'q', memsize, true);
    -
    106 * if(shmid < 0) return -1; // creation failed
    -
    107 * void *memory = qshm_get(shmid);
    -
    108 *
    -
    109 * // initialize hash-table
    -
    110 * qhasharr_t *tbl = qhasharr(memory, memsize);
    -
    111 * if(hasharr == NULL) return -1;
    -
    112 *
    -
    113 * (...your codes with your own locking mechanism...)
    -
    114 *
    -
    115 * // Release reference object
    -
    116 * tbl->free(tbl);
    -
    117 *
    -
    118 * // destroy shared memory
    -
    119 * qshm_free(shmid);
    -
    120 *
    -
    121 * [USER SIDE]
    -
    122 * int shmid = qshm_getid("/tmp/some_id_file", 'q');
    -
    123 *
    -
    124 * // get shared memory
    -
    125 * void *memory = qshm_get(shmid);
    -
    126 *
    -
    127 * // map existing memory into table
    -
    128 * qhasharr_t *tbl = qhasharr(memory, 0);
    -
    129 *
    -
    130 * (...your codes with your own locking mechanism...)
    -
    131 *
    -
    132 * // Release reference object
    -
    133 * tbl->free(tbl);
    -
    134 * @endcode
    -
    135 */
    -
    136
    -
    137#include <stdio.h>
    -
    138#include <stdlib.h>
    -
    139#include <stdbool.h>
    -
    140#include <stdint.h>
    -
    141#include <inttypes.h>
    -
    142#include <stdarg.h>
    -
    143#include <string.h>
    -
    144#include <errno.h>
    -
    145#include <assert.h>
    -
    146#include "qinternal.h"
    -
    147#include "utilities/qhash.h"
    -
    148#include "containers/qhasharr.h"
    -
    149
    -
    150#define COLLISION_MARK (-1)
    -
    151#define EXTBLOCK_MARK (-2)
    -
    152
    -
    153#ifndef _DOXYGEN_SKIP
    -
    154
    -
    155static qhasharr_slot_t* get_slots(qhasharr_t *tbl);
    -
    156static int find_avail(qhasharr_t *tbl, int startidx);
    -
    157static int get_idx(qhasharr_t *tbl, const void *name, size_t namesize,
    -
    158 uint32_t hash);
    -
    159static void *get_data(qhasharr_t *tbl, int idx, size_t *size);
    -
    160static bool put_data(qhasharr_t *tbl, int idx, uint32_t hash, const void *name,
    -
    161 size_t namesize, const void *data, size_t datasize,
    -
    162 int count);
    -
    163static bool copy_slot(qhasharr_t *tbl, int idx1, int idx2);
    -
    164static bool remove_slot(qhasharr_t *tbl, int idx);
    -
    165static bool remove_data(qhasharr_t *tbl, int idx);
    -
    166
    -
    167#endif
    -
    168
    -
    169/**
    -
    170 * Get how much memory is needed for N slots.
    -
    171 *
    -
    172 * @param max a number of maximum internal slots
    -
    173 *
    -
    174 * @return memory size needed
    -
    175 *
    -
    176 * @note
    -
    177 * This can be used for calculating minimum memory size for N slots.
    -
    178 */
    - -
    180 size_t memsize = sizeof(qhasharr_data_t)
    -
    181 + (sizeof(qhasharr_slot_t) * (max));
    -
    182 return memsize;
    -
    183}
    -
    184
    -
    185/**
    -
    186 * Initialize static hash table
    -
    187 *
    -
    188 * @param memory a pointer of data memory.
    -
    189 * @param memsize a size of data memory, 0 for using existing data.
    -
    190 *
    -
    191 * @return qhasharr_t container pointer, otherwise returns NULL.
    -
    192 * @retval errno will be set in error condition.
    -
    193 * - EINVAL : Assigned memory is too small. It must bigger enough to allocate
    -
    194 * at least 1 slot.
    -
    195 *
    -
    196 * @code
    -
    197 * // initialize hash-table with 100 slots.
    -
    198 * // A single element can take several slots.
    -
    199 * char memory[112 * 100];
    -
    200 *
    -
    201 * // Initialize new table.
    -
    202 * qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    -
    203 *
    -
    204 * // Use existing table.
    -
    205 * qhasharr_t *tbl2 = qhasharr(memory, 0);
    -
    206 * @endcode
    -
    207 */
    -
    208qhasharr_t *qhasharr(void *memory, size_t memsize) {
    -
    209 // Structure memory.
    -
    210 qhasharr_data_t *tbldata = (qhasharr_data_t *) memory;
    -
    211
    -
    212 // Initialize data if memsize is set or use existing data.
    -
    213 if (memsize > 0) {
    -
    214 // calculate max
    -
    215 int maxslots = (memsize - sizeof(qhasharr_data_t))
    -
    216 / sizeof(qhasharr_slot_t);
    -
    217 if (maxslots < 1 || memsize <= sizeof(qhasharr_t)) {
    -
    218 errno = EINVAL;
    -
    219 return NULL;
    -
    220 }
    -
    221
    -
    222 // Set memory.
    -
    223 memset((void *) tbldata, 0, memsize);
    -
    224 tbldata->maxslots = maxslots;
    -
    225 tbldata->usedslots = 0;
    -
    226 tbldata->num = 0;
    -
    227 }
    -
    228
    -
    229 // Create the table object.
    -
    230 qhasharr_t *tbl = (qhasharr_t *) malloc(sizeof(qhasharr_t));
    -
    231 if (tbl == NULL) {
    -
    232 errno = ENOMEM;
    -
    233 return NULL;
    -
    234 }
    -
    235 memset((void *) tbl, 0, sizeof(qhasharr_t));
    -
    236
    -
    237 // assign methods
    -
    238 tbl->put = qhasharr_put;
    -
    239 tbl->putstr = qhasharr_putstr;
    -
    240 tbl->putstrf = qhasharr_putstrf;
    -
    241 tbl->put_by_obj = qhasharr_put_by_obj;
    -
    242
    -
    243 tbl->get = qhasharr_get;
    -
    244 tbl->getstr = qhasharr_getstr;
    -
    245 tbl->get_by_obj = qhasharr_get_by_obj;
    -
    246
    -
    247 tbl->remove = qhasharr_remove;
    -
    248 tbl->remove_by_obj = qhasharr_remove_by_obj;
    -
    249 tbl->remove_by_idx = qhasharr_remove_by_idx;
    -
    250
    -
    251 tbl->getnext = qhasharr_getnext;
    -
    252
    -
    253 tbl->size = qhasharr_size;
    -
    254 tbl->clear = qhasharr_clear;
    -
    255 tbl->debug = qhasharr_debug;
    -
    256
    -
    257 tbl->free = qhasharr_free;
    -
    258
    -
    259 tbl->data = tbldata;
    -
    260
    -
    261 return tbl;
    -
    262}
    -
    263
    -
    264/**
    -
    265 * qhasharr->put(): Put an object into this table.
    -
    266 *
    -
    267 * @param tbl qhasharr_t container pointer.
    -
    268 * @param key key string
    -
    269 * @param value value object data
    -
    270 * @param size size of value
    -
    271 *
    -
    272 * @return true if successful, otherwise returns false
    -
    273 * @retval errno will be set in error condition.
    -
    274 * - ENOBUFS : Table doesn't have enough space to store the object.
    -
    275 * - EINVAL : Invalid argument.
    -
    276 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    277 */
    -
    278bool qhasharr_put(qhasharr_t *tbl, const char *name, const void *data,
    -
    279 size_t datasize) {
    -
    280 return qhasharr_put_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0, data,
    -
    281 datasize);
    -
    282}
    -
    283
    -
    284/**
    -
    285 * qhasharr->putstr(): Put a string into this table
    -
    286 *
    -
    287 * @param tbl qhasharr_t container pointer.
    -
    288 * @param name key string.
    -
    289 * @param data value string.
    -
    290 *
    -
    291 * @return true if successful, otherwise returns false
    -
    292 * @retval errno will be set in error condition.
    -
    293 * - ENOBUFS : Table doesn't have enough space to store the object.
    -
    294 * - EINVAL : Invalid argument.
    -
    295 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    296 */
    -
    297bool qhasharr_putstr(qhasharr_t *tbl, const char *name, const char *data) {
    -
    298 return qhasharr_put_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0,
    -
    299 data, (data) ? strlen(data) + 1 : 0);
    -
    300}
    -
    301
    -
    302/**
    -
    303 * qhasharr->putstrf(): Put a formatted string into this table.
    -
    304 *
    -
    305 * @param tbl qhasharr_t container pointer.
    -
    306 * @param name key string
    -
    307 * @param format formatted string data.
    -
    308 *
    -
    309 * @return true if successful, otherwise returns false.
    -
    310 * @retval errno will be set in error condition.
    -
    311 * - ENOBUFS : Table doesn't have enough space to store the object.
    -
    312 * - ENOMEM : System memory allocation failure.
    -
    313 * - EINVAL : Invalid argument.
    -
    314 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    315 */
    -
    316bool qhasharr_putstrf(qhasharr_t *tbl, const char *name, const char *format, ...) {
    -
    317 char *str;
    -
    318 DYNAMIC_VSPRINTF(str, format);
    -
    319 if (str == NULL) {
    -
    320 errno = ENOMEM;
    -
    321 return false;
    -
    322 }
    -
    323
    -
    324 bool ret = qhasharr_putstr(tbl, name, str);
    -
    325 free(str);
    -
    326 return ret;
    -
    327}
    -
    328
    -
    329/**
    -
    330 * qhasharr->put_by_obj(): ut an object into this table by key object.
    -
    331 *
    -
    332 * @param tbl qhasharr_t container pointer.
    -
    333 * @param name key data
    -
    334 * @param namesize size of key
    -
    335 * @param data data
    -
    336 * @param datasize size of data
    -
    337 *
    -
    338 * @return true if successful, otherwise returns false
    -
    339 * @retval errno will be set in error condition.
    -
    340 * - ENOBUFS : Table doesn't have enough space to store the object.
    -
    341 * - EINVAL : Invalid argument.
    -
    342 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    343 */
    -
    344bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize,
    -
    345 const void *data, size_t datasize) {
    -
    346 if (tbl == NULL || name == NULL || namesize == 0 || data == NULL
    -
    347 || datasize == 0) {
    -
    348 errno = EINVAL;
    -
    349 return false;
    -
    350 }
    -
    351
    -
    352 qhasharr_data_t *tbldata = tbl->data;
    -
    353 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    354
    -
    355 // check full
    -
    356 if (tbldata->usedslots >= tbldata->maxslots) {
    -
    357 errno = ENOBUFS;
    -
    358 return false;
    -
    359 }
    -
    360
    -
    361 // get hash integer
    -
    362 uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    -
    363
    -
    364 // check, is slot empty
    -
    365 if (tblslots[hash].count == 0) { // empty slot
    -
    366 // put data
    -
    367 if (put_data(tbl, hash, hash, name, namesize, data, datasize,
    -
    368 1) == false) {
    -
    369 return false;
    -
    370 }
    -
    371 } else if (tblslots[hash].count > 0) { // same key or hash collision
    -
    372 // check same key;
    -
    373 int idx = get_idx(tbl, name, namesize, hash);
    -
    374 if (idx >= 0) { // same key
    -
    375 // remove and recall
    -
    376 qhasharr_remove_by_idx(tbl, idx);
    -
    377 return qhasharr_put_by_obj(tbl, name, namesize, data, datasize);
    -
    378 } else { // no same key but hash collision
    -
    379 // find empty slot
    -
    380 int idx = find_avail(tbl, hash);
    -
    381 if (idx < 0) {
    -
    382 errno = ENOBUFS;
    -
    383 return false;
    -
    384 }
    -
    385
    -
    386 // put data. -1 is used for collision resolution (idx != hash);
    -
    387 if (put_data(tbl, idx, hash, name, namesize, data, datasize,
    -
    388 COLLISION_MARK) == false) {
    -
    389 return false;
    -
    390 }
    -
    391
    -
    392 // increase counter from leading slot
    -
    393 tblslots[hash].count++;
    -
    394 }
    -
    395 } else {
    -
    396 // collision key or extended block
    -
    397
    -
    398 // find empty slot
    -
    399 int idx = find_avail(tbl, hash + 1);
    -
    400 if (idx < 0) {
    -
    401 errno = ENOBUFS;
    -
    402 return false;
    -
    403 }
    -
    404
    -
    405 // move the slot
    -
    406 copy_slot(tbl, idx, hash);
    -
    407 remove_slot(tbl, hash);
    -
    408
    -
    409 // adjust the link chain
    -
    410 if (tblslots[idx].link != -1) {
    -
    411 tblslots[tblslots[idx].link].hash = idx;
    -
    412 }
    -
    413 if (tblslots[idx].count == EXTBLOCK_MARK) {
    -
    414 tblslots[tblslots[idx].hash].link = idx;
    -
    415 }
    -
    416
    -
    417 // store data
    -
    418 if (put_data(tbl, hash, hash, name, namesize, data, datasize,
    -
    419 1) == false) {
    -
    420 return false;
    -
    421 }
    -
    422 }
    -
    423
    -
    424 return true;
    -
    425}
    -
    426
    -
    427/**
    -
    428 * qhasharr->get(): Get an object from this table
    -
    429 *
    -
    430 * @param tbl qhasharr_t container pointer.
    -
    431 * @param name key string
    -
    432 * @param datasize if not NULL, returned object size will be stored
    -
    433 *
    -
    434 * @return malloced object pointer if successful, otherwise(not found)
    -
    435 * returns NULL
    -
    436 * @retval errno will be set in error condition.
    -
    437 * - ENOENT : No such key found.
    -
    438 * - EINVAL : Invalid argument.
    -
    439 * - ENOMEM : Memory allocation failed.
    -
    440 *
    -
    441 * @note
    -
    442 * returned object must be freed after done using.
    -
    443 */
    -
    444void *qhasharr_get(qhasharr_t *tbl, const char *name, size_t *datasize) {
    -
    445 return qhasharr_get_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0, datasize);
    -
    446}
    -
    447
    -
    448/**
    -
    449 * qhasharr->getstr(): Finds an object with given name and returns as
    -
    450 * string type.
    -
    451 *
    -
    452 * @param tbl qhasharr_t container pointer.
    -
    453 * @param name key string
    -
    454 *
    -
    455 * @return string pointer if successful, otherwise(not found) returns NULL
    -
    456 * @retval errno will be set in error condition.
    -
    457 * - ENOENT : No such key found.
    -
    458 * - EINVAL : Invalid argument.
    -
    459 * - ENOMEM : Memory allocation failed.
    -
    460 *
    -
    461 * @note
    -
    462 * returned object must be freed after done using.
    -
    463 */
    -
    464char *qhasharr_getstr(qhasharr_t *tbl, const char *name) {
    -
    465 return (char *) qhasharr_get(tbl, name, NULL);
    -
    466}
    -
    467
    -
    468/**
    -
    469 * qhasharr->get_by_object(): Get an object from this table by key object
    -
    470 *
    -
    471 * @param tbl qhasharr_t container pointer.
    -
    472 * @param name key data
    -
    473 * @param namesize size of key
    -
    474 * @param datasize if not NULL, returned object size will be stored
    -
    475 *
    -
    476 * @return malloced object pointer if successful, otherwise(not found)
    -
    477 * returns NULL
    -
    478 * @retval errno will be set in error condition.
    -
    479 * - ENOENT : No such key found.
    -
    480 * - EINVAL : Invalid argument.
    -
    481 * - ENOMEM : Memory allocation failed.
    -
    482 *
    -
    483 * @note
    -
    484 * returned object must be freed after done using.
    -
    485 */
    -
    486void *qhasharr_get_by_obj(qhasharr_t *tbl, const void *name, size_t namesize,
    -
    487 size_t *datasize) {
    -
    488 if (tbl == NULL || name == NULL || namesize == 0) {
    -
    489 errno = EINVAL;
    -
    490 return NULL;
    -
    491 }
    -
    492
    -
    493 qhasharr_data_t *tbldata = tbl->data;
    -
    494
    -
    495 // get hash integer
    -
    496 uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    -
    497 int idx = get_idx(tbl, name, namesize, hash);
    -
    498 if (idx < 0) {
    -
    499 errno = ENOENT;
    -
    500 return NULL;
    -
    501 }
    -
    502
    -
    503 return get_data(tbl, idx, datasize);
    -
    504}
    -
    505
    -
    506/**
    -
    507 * qhasharr->remove(): Remove an object from this table.
    -
    508 *
    -
    509 * @param tbl qhasharr_t container pointer.
    -
    510 * @param name key string
    -
    511 *
    -
    512 * @return true if successful, otherwise(not found) returns false
    -
    513 * @retval errno will be set in error condition.
    -
    514 * - ENOENT : No such key found.
    -
    515 * - EINVAL : Invald argument.
    -
    516 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    517 */
    -
    518bool qhasharr_remove(qhasharr_t *tbl, const char *name) {
    -
    519 return qhasharr_remove_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0);
    -
    520}
    -
    521
    -
    522/**
    -
    523 * qhasharr->remove_by_obj(): Remove an object from this table by key object
    -
    524 *
    -
    525 * @param tbl qhasharr_t container pointer.
    -
    526 * @param name key data
    -
    527 * @param namesize size of key
    -
    528 *
    -
    529 * @return true if successful, otherwise(not found) returns false
    -
    530 * @retval errno will be set in error condition.
    -
    531 * - ENOENT : No such key found.
    -
    532 * - EINVAL : Invald argument.
    -
    533 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    534 */
    -
    535bool qhasharr_remove_by_obj(qhasharr_t *tbl, const char *name, size_t namesize) {
    -
    536 if (tbl == NULL || name == NULL || namesize == 0) {
    -
    537 errno = EINVAL;
    -
    538 return false;
    -
    539 }
    -
    540
    -
    541 qhasharr_data_t *tbldata = tbl->data;
    -
    542
    -
    543 // get hash integer
    -
    544 uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    -
    545 int idx = get_idx(tbl, name, namesize, hash);
    -
    546 if (idx < 0) {
    -
    547 errno = ENOENT;
    -
    548 return false;
    -
    549 }
    -
    550
    -
    551 return qhasharr_remove_by_idx(tbl, idx);
    -
    552}
    -
    553
    -
    554/**
    -
    555 * qhasharr->remove_by_idx(): Remove an object from this table by index number.
    -
    556 *
    -
    557 * @param tbl qhasharr_t container pointer.
    -
    558 * @param idx slot index number
    -
    559 *
    -
    560 * @return true if successful, otherwise(not found) returns false
    -
    561 * @retval errno will be set in error condition.
    -
    562 * - ENOENT : Index is not pointing a valid object.
    -
    563 * - EINVAL : Invald argument.
    -
    564 * - EFAULT : Unexpected error. Data structure is not constant.
    -
    565 *
    -
    566 * @code
    -
    567 * int idx = 0;
    -
    568 * qhasharr_obj_t obj;
    -
    569 * while(tbl->getnext(tbl, &obj, &idx) == true) {
    -
    570 * if (condition_to_remove) {
    -
    571 * idx--; // adjust index by -1
    -
    572 * remove_by_idx(idx);
    -
    573 * }
    -
    574 * free(obj.name);
    -
    575 * free(obj.data);
    -
    576 * }
    -
    577 * @endcode
    -
    578 *
    -
    579 * @note
    -
    580 * This function is to remove an object in getnext() traversal loop without
    -
    581 * knowing the object name but index value. When key names are longer than
    -
    582 * defined size, the keys are stored with truncation with their fingerprint,
    -
    583 * so this method provides a way to remove those keys.
    -
    584 * getnext() returns actual index + 1(pointing next slot of current finding),
    -
    585 * so you need to adjust it by -1 for the valid index number. And once you
    -
    586 * remove object by this method, rewind idx by -1 before calling getnext()
    -
    587 * because collision objects can be moved back to removed index again, so
    -
    588 * by adjusting index by -1, getnext() can continue search from the removed
    -
    589 * slot index again. Please refer an example code.
    -
    590 */
    -
    591bool qhasharr_remove_by_idx(qhasharr_t *tbl, int idx) {
    -
    592 if (idx < 0) {
    -
    593 errno = EINVAL;
    -
    594 return false;
    -
    595 }
    -
    596
    -
    597 qhasharr_data_t *tbldata = tbl->data;
    -
    598 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    599
    -
    600 if (tblslots[idx].count == 1) {
    -
    601 // just remove
    -
    602 remove_data(tbl, idx);
    -
    603 } else if (tblslots[idx].count > 1) { // leading slot and has collision
    -
    604 // find the collision key
    -
    605 int idx2;
    -
    606 for (idx2 = idx + 1;; idx2++) {
    -
    607 if (idx2 >= tbldata->maxslots)
    -
    608 idx2 = 0;
    -
    609 if (idx2 == idx) {
    -
    610 errno = EFAULT;
    -
    611 return false;
    -
    612 }
    -
    613 if (tblslots[idx2].count == COLLISION_MARK
    -
    614 && tblslots[idx2].hash == tblslots[idx].hash) {
    -
    615 break;
    -
    616 }
    -
    617 }
    -
    618
    -
    619 // move to leading slot
    -
    620 int backupcount = tblslots[idx].count;
    -
    621 remove_data(tbl, idx); // remove leading data
    -
    622 copy_slot(tbl, idx, idx2); // copy slot
    -
    623 remove_slot(tbl, idx2); // remove moved slot
    -
    624
    -
    625 tblslots[idx].count = backupcount - 1; // adjust collision counter
    -
    626 if (tblslots[idx].link != -1) {
    -
    627 tblslots[tblslots[idx].link].hash = idx;
    -
    628 }
    -
    629
    -
    630 } else if (tblslots[idx].count == COLLISION_MARK) { // collision key
    -
    631 // decrease counter from leading slot
    -
    632 if (tblslots[tblslots[idx].hash].count <= 1) {
    -
    633 errno = EFAULT;
    -
    634 return false;
    -
    635 }
    -
    636 tblslots[tblslots[idx].hash].count--;
    -
    637
    -
    638 // remove data
    -
    639 remove_data(tbl, idx);
    -
    640 } else {
    -
    641 errno = ENOENT;
    -
    642 return false;
    -
    643 }
    -
    644
    -
    645 return true;
    -
    646}
    -
    647
    -
    648/**
    -
    649 * qhasharr->getnext(): Get next element.
    -
    650 *
    -
    651 * @param tbl qhasharr_t container pointer.
    -
    652 * @param idx index pointer
    -
    653 *
    -
    654 * @return key name string if successful, otherwise(end of table) returns NULL
    -
    655 * @retval errno will be set in error condition.
    -
    656 * - ENOENT : No next element.
    -
    657 * - EINVAL : Invald argument.
    -
    658 * - ENOMEM : Memory allocation failed.
    -
    659 *
    -
    660 * @code
    -
    661 * int idx = 0;
    -
    662 * qhasharr_obj_t obj;
    -
    663 * while(tbl->getnext(tbl, &obj, &idx) == true) {
    -
    664 * printf("NAMESIZE=%zu, DATASIZE=%zu\n",
    -
    665 * obj.namesize, obj.datasize);
    -
    666 * free(obj.name); // key name
    -
    667 * free(obj.data); // value data
    -
    668 * }
    -
    669 * @endcode
    -
    670 *
    -
    671 * @note
    -
    672 * Please be aware a key name will be returned with truncated length
    -
    673 * because key name gets truncated if it doesn't fit into slot size,
    -
    674 * Q_HASHARR_NAMESIZE.
    -
    675 */
    -
    676bool qhasharr_getnext(qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx) {
    -
    677 if (tbl == NULL || obj == NULL || idx == NULL) {
    -
    678 errno = EINVAL;
    -
    679 return NULL;
    -
    680 }
    -
    681
    -
    682 qhasharr_data_t *tbldata = tbl->data;
    -
    683 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    684
    -
    685 for (; *idx < tbldata->maxslots; (*idx)++) {
    -
    686 if (tblslots[*idx].count == 0 || tblslots[*idx].count == EXTBLOCK_MARK) {
    -
    687 continue;
    -
    688 }
    -
    689
    -
    690 size_t namesize = tblslots[*idx].data.pair.namesize;
    -
    691 if (namesize > Q_HASHARR_NAMESIZE)
    -
    692 namesize = Q_HASHARR_NAMESIZE;
    -
    693
    -
    694 obj->name = malloc(namesize + 1);
    -
    695 if (obj->name == NULL) {
    -
    696 errno = ENOMEM;
    -
    697 return false;
    -
    698 }
    -
    699 memcpy(obj->name, tblslots[*idx].data.pair.name, namesize);
    -
    700 memcpy(obj->name + namesize, "", 1); // for truncated case
    -
    701 obj->namesize = namesize;
    -
    702
    -
    703 obj->data = get_data(tbl, *idx, &obj->datasize);
    -
    704 if (obj->data == NULL) {
    -
    705 free(obj->name);
    -
    706 errno = ENOMEM;
    -
    707 return false;
    -
    708 }
    -
    709
    -
    710 *idx += 1;
    -
    711 return true;
    -
    712 }
    -
    713
    -
    714 errno = ENOENT;
    -
    715 return false;
    -
    716}
    -
    717
    -
    718/**
    -
    719 * qhasharr->size(): Returns the number of objects in this table.
    -
    720 *
    -
    721 * @param tbl qhasharr_t container pointer.
    -
    722 * @param maxslots if not NULL, total number of slots will be stored.
    -
    723 * @param usedslots if not NULL, total number of used slots will be stored.
    -
    724 *
    -
    725 * @return a number of elements stored.
    -
    726 */
    -
    727int qhasharr_size(qhasharr_t *tbl, int *maxslots, int *usedslots) {
    -
    728 if (tbl == NULL) {
    -
    729 errno = EINVAL;
    -
    730 return -1;
    -
    731 }
    -
    732
    -
    733 qhasharr_data_t *tbldata = tbl->data;
    -
    734
    -
    735 if (maxslots != NULL)
    -
    736 *maxslots = tbldata->maxslots;
    -
    737 if (usedslots != NULL)
    -
    738 *usedslots = tbldata->usedslots;
    -
    739
    -
    740 return tbldata->num;
    -
    741}
    -
    742
    -
    743/**
    -
    744 * qhasharr->clear(): Clears this table so that it contains no keys.
    -
    745 *
    -
    746 * @param tbl qhasharr_t container pointer.
    -
    747 *
    -
    748 * @return true if successful, otherwise returns false.
    -
    749 */
    -
    750void qhasharr_clear(qhasharr_t *tbl) {
    -
    751 if (tbl == NULL) {
    -
    752 errno = EINVAL;
    -
    753 return;
    -
    754 }
    -
    755
    -
    756 qhasharr_data_t *tbldata = tbl->data;
    -
    757 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    758
    -
    759 if (tbldata->usedslots == 0)
    -
    760 return;
    -
    761
    -
    762 tbldata->usedslots = 0;
    -
    763 tbldata->num = 0;
    -
    764
    -
    765 // clear memory
    -
    766 memset((void *) tblslots, '\0',
    -
    767 (tbldata->maxslots * sizeof(qhasharr_slot_t)));
    -
    768}
    -
    769
    -
    770/**
    -
    771 * qhasharr->debug(): Print hash table for debugging purpose
    -
    772 *
    -
    773 * @param tbl qhasharr_t container pointer.
    -
    774 * @param out output stream
    -
    775 *
    -
    776 * @return true if successful, otherwise returns false
    -
    777 * @retval errno will be set in error condition.
    -
    778 * - EIO : Invalid output stream.
    -
    779 */
    -
    780bool qhasharr_debug(qhasharr_t *tbl, FILE *out) {
    -
    781 if (tbl == NULL) {
    -
    782 errno = EINVAL;
    -
    783 return false;
    -
    784 }
    -
    785
    -
    786 if (out == NULL) {
    -
    787 errno = EIO;
    -
    788 return false;
    -
    789 }
    -
    790
    -
    791 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    792
    -
    793 int idx = 0;
    -
    794 qhasharr_obj_t obj;
    -
    795 while (tbl->getnext(tbl, &obj, &idx) == true) {
    -
    796 uint16_t namesize = tblslots[idx - 1].data.pair.namesize;
    -
    797 _q_textout(out, obj.name, obj.namesize, MAX_HUMANOUT);
    -
    798 fprintf(out, "%s(%d)=", (namesize > Q_HASHARR_NAMESIZE) ? "..." : "",
    -
    799 namesize);
    -
    800 _q_textout(out, obj.data, obj.datasize, MAX_HUMANOUT);
    -
    801 fprintf(out, " (%zu)\n", obj.datasize);
    -
    802
    -
    803 free(obj.name);
    -
    804 free(obj.data);
    -
    805 }
    -
    806
    -
    807#ifdef BUILD_DEBUG
    -
    808 qhasharr_data_t *tbldata = tbl->data;
    -
    809 fprintf(out, "%d elements (slot %d used/%d total)\n",
    -
    810 tbldata->num, tbldata->usedslots, tbldata->maxslots);
    -
    811 for (idx = 0; idx < tbldata->maxslots; idx++) {
    -
    812 if (tblslots[idx].count == 0) continue;
    -
    813
    -
    814 fprintf(out, "slot=%d,type=", idx);
    -
    815 if (tblslots[idx].count == EXTBLOCK_MARK) {
    -
    816 fprintf(out, "EXTEND");
    -
    817 fprintf(out, ",prev=%d", tblslots[idx].hash);
    -
    818 fprintf(out, ",next=%d", tblslots[idx].link);
    -
    819 fprintf(out, ",datasize=%d", tblslots[idx].datasize);
    -
    820 fprintf(out, ",data=");
    -
    821 _q_textout(out,
    -
    822 tblslots[idx].data.ext.data,
    -
    823 tblslots[idx].datasize,
    -
    824 MAX_HUMANOUT);
    -
    825 } else {
    -
    826 fprintf(out, "%s", (tblslots[idx].count == COLLISION_MARK)?"COLISN":"NORMAL");
    -
    827 fprintf(out, ",next=%d", tblslots[idx].link);
    -
    828 fprintf(out, ",count=%d", tblslots[idx].count);
    -
    829 fprintf(out, ",hash=%u", tblslots[idx].hash);
    -
    830 fprintf(out, ",namesize=%d", tblslots[idx].data.pair.namesize);
    -
    831 fprintf(out, ",datasize=%d", tblslots[idx].datasize);
    -
    832 fprintf(out, ",name=");
    -
    833 _q_textout(out,
    -
    834 tblslots[idx].data.pair.name,
    -
    835 (tblslots[idx].data.pair.namesize>Q_HASHARR_NAMESIZE)
    -
    836 ? Q_HASHARR_NAMESIZE
    -
    837 : tblslots[idx].data.pair.namesize,
    -
    838 MAX_HUMANOUT);
    -
    839 fprintf(out, ",data=");
    -
    840 _q_textout(out,
    -
    841 tblslots[idx].data.pair.data,
    -
    842 tblslots[idx].datasize,
    -
    843 MAX_HUMANOUT);
    -
    844 }
    -
    845 fprintf(out, "\n");
    -
    846 }
    -
    847#endif
    -
    848
    -
    849 return true;
    -
    850}
    -
    851
    -
    852/**
    -
    853 * qhasharr->free(): De-allocate table reference object.
    -
    854 *
    -
    855 * @param tbl qhashtbl_t container pointer.
    -
    856 *
    -
    857 * @note
    -
    858 * This does not de-allocate the data memory but only the memory of
    -
    859 * qhasharr struct. User provided data memory must be de-allocated
    -
    860 * by user.
    -
    861 */
    -
    862void qhasharr_free(qhasharr_t *tbl) {
    -
    863 free(tbl);
    -
    864}
    -
    865
    -
    866#ifndef _DOXYGEN_SKIP
    -
    867
    -
    868static qhasharr_slot_t* get_slots(qhasharr_t *tbl) {
    -
    869 return (qhasharr_slot_t*) ((char*) (tbl->data) + sizeof(qhasharr_data_t));
    -
    870}
    -
    871
    -
    872// find empty slot : return empty slow number, otherwise returns -1.
    -
    873static int find_avail(qhasharr_t *tbl, int startidx) {
    -
    874 qhasharr_data_t *tbldata = tbl->data;
    -
    875 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    876
    -
    877 if (startidx >= tbldata->maxslots)
    -
    878 startidx = 0;
    -
    879
    -
    880 int idx = startidx;
    -
    881 while (true) {
    -
    882 if (tblslots[idx].count == 0)
    -
    883 return idx;
    -
    884
    -
    885 idx++;
    -
    886 if (idx >= tbldata->maxslots)
    -
    887 idx = 0;
    -
    888 if (idx == startidx)
    -
    889 break;
    -
    890 }
    -
    891
    -
    892 return -1;
    -
    893}
    -
    894
    -
    895static int get_idx(qhasharr_t *tbl, const void *name, size_t namesize,
    -
    896 uint32_t hash) {
    -
    897 qhasharr_data_t *tbldata = tbl->data;
    -
    898 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    899
    -
    900 if (tblslots[hash].count > 0) {
    -
    901 int count, idx;
    -
    902 for (count = 0, idx = hash; count < tblslots[hash].count;) {
    -
    903 if (tblslots[idx].hash == hash
    -
    904 && (tblslots[idx].count > 0 || tblslots[idx].count == COLLISION_MARK)) {
    -
    905 // same hash
    -
    906 count++;
    -
    907
    -
    908 // is same key?
    -
    909 // first check key length
    -
    910 if (namesize == tblslots[idx].data.pair.namesize) {
    -
    911 if (namesize <= Q_HASHARR_NAMESIZE) {
    -
    912 // original key is stored
    -
    913 if (!memcmp(name, tblslots[idx].data.pair.name,
    -
    914 namesize)) {
    -
    915 return idx;
    -
    916 }
    -
    917 } else {
    -
    918 // key is truncated, compare MD5 also.
    -
    919 unsigned char namemd5[16];
    -
    920 qhashmd5(name, namesize, namemd5);
    -
    921 if (!memcmp(name, tblslots[idx].data.pair.name,
    -
    922 Q_HASHARR_NAMESIZE)
    -
    923 && !memcmp(namemd5,
    -
    924 tblslots[idx].data.pair.namemd5,
    -
    925 16)) {
    -
    926 return idx;
    -
    927 }
    -
    928 }
    -
    929 }
    -
    930 }
    -
    931
    -
    932 // increase idx
    -
    933 idx++;
    -
    934 if (idx >= tbldata->maxslots)
    -
    935 idx = 0;
    -
    936
    -
    937 // check loop
    -
    938 if (idx == hash)
    -
    939 break;
    -
    940
    -
    941 continue;
    -
    942 }
    -
    943 }
    -
    944
    -
    945 return -1;
    -
    946}
    -
    947
    -
    948static void *get_data(qhasharr_t *tbl, int idx, size_t *size) {
    -
    949 if (idx < 0) {
    -
    950 errno = ENOENT;
    -
    951 return NULL;
    -
    952 }
    -
    953
    -
    954 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    955
    -
    956 int newidx;
    -
    957 size_t datasize;
    -
    958 for (newidx = idx, datasize = 0;; newidx = tblslots[newidx].link) {
    -
    959 datasize += tblslots[newidx].datasize;
    -
    960 if (tblslots[newidx].link == -1)
    -
    961 break;
    -
    962 }
    -
    963
    -
    964 void *data, *dp;
    -
    965 if ((data = malloc(datasize)) == NULL) {
    -
    966 errno = ENOMEM;
    -
    967 return NULL;
    -
    968 }
    -
    969
    -
    970 for (newidx = idx, dp = data;; newidx = tblslots[newidx].link) {
    -
    971 if (tblslots[newidx].count == EXTBLOCK_MARK) {
    -
    972 // extended data block
    -
    973 memcpy(dp, (void *) tblslots[newidx].data.ext.data,
    -
    974 tblslots[newidx].datasize);
    -
    975 } else {
    -
    976 // key/value pair data block
    -
    977 memcpy(dp, (void *) tblslots[newidx].data.pair.data,
    -
    978 tblslots[newidx].datasize);
    -
    979 }
    -
    980
    -
    981 dp += tblslots[newidx].datasize;
    -
    982 if (tblslots[newidx].link == -1)
    -
    983 break;
    -
    984 }
    -
    985
    -
    986 if (size != NULL)
    -
    987 *size = datasize;
    -
    988 return data;
    -
    989}
    -
    990
    -
    991static bool put_data(qhasharr_t *tbl, int idx, uint32_t hash, const void *name,
    -
    992 size_t namesize, const void *data, size_t datasize,
    -
    993 int count) {
    -
    994 qhasharr_data_t *tbldata = tbl->data;
    -
    995 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    996
    -
    997 assert(tblslots[idx].count == 0);
    -
    998
    -
    999 unsigned char namemd5[16];
    -
    1000 qhashmd5(name, namesize, namemd5);
    -
    1001
    -
    1002 // store name
    -
    1003 tblslots[idx].count = count;
    -
    1004 tblslots[idx].hash = hash;
    -
    1005 memcpy(tblslots[idx].data.pair.name, name,
    -
    1006 (namesize < Q_HASHARR_NAMESIZE) ? namesize : Q_HASHARR_NAMESIZE);
    -
    1007 memcpy((char *) tblslots[idx].data.pair.namemd5, (char *) namemd5, 16);
    -
    1008 tblslots[idx].data.pair.namesize = namesize;
    -
    1009 tblslots[idx].link = -1;
    -
    1010
    -
    1011 // store data
    -
    1012 int newidx;
    -
    1013 size_t savesize;
    -
    1014 for (newidx = idx, savesize = 0; savesize < datasize;) {
    -
    1015 if (savesize > 0) { // find next empty slot
    -
    1016 int tmpidx = find_avail(tbl, newidx + 1);
    -
    1017 if (tmpidx < 0) {
    -
    1018 remove_data(tbl, idx);
    -
    1019 errno = ENOBUFS;
    -
    1020 return false;
    -
    1021 }
    -
    1022
    -
    1023 // clear & set
    -
    1024 memset((void *) (&tblslots[tmpidx]), '\0', sizeof(qhasharr_slot_t));
    -
    1025
    -
    1026 tblslots[tmpidx].count = EXTBLOCK_MARK; // extended data block
    -
    1027 tblslots[tmpidx].hash = newidx; // previous link
    -
    1028 tblslots[tmpidx].link = -1; // end block mark
    -
    1029 tblslots[tmpidx].datasize = 0;
    -
    1030 tblslots[newidx].link = tmpidx; // link chain
    -
    1031
    -
    1032 newidx = tmpidx;
    -
    1033 }
    -
    1034
    -
    1035 // copy data
    -
    1036 size_t copysize = datasize - savesize;
    -
    1037 if (tblslots[newidx].count == EXTBLOCK_MARK) {
    -
    1038 // extended value
    -
    1039 if (copysize > sizeof(struct Q_HASHARR_SLOT_EXT)) {
    -
    1040 copysize = sizeof(struct Q_HASHARR_SLOT_EXT);
    -
    1041 }
    -
    1042 memcpy(tblslots[newidx].data.ext.data, data + savesize, copysize);
    -
    1043 } else {
    -
    1044 // first slot
    -
    1045 if (copysize > Q_HASHARR_DATASIZE) {
    -
    1046 copysize = Q_HASHARR_DATASIZE;
    -
    1047 }
    -
    1048 memcpy(tblslots[newidx].data.pair.data, data + savesize, copysize);
    -
    1049
    -
    1050 // increase stored key counter
    -
    1051 tbldata->num++;
    -
    1052 }
    -
    1053 tblslots[newidx].datasize = copysize;
    -
    1054 savesize += copysize;
    -
    1055
    -
    1056 // increase used slot counter
    -
    1057 tbldata->usedslots++;
    -
    1058 }
    -
    1059
    -
    1060 return true;
    -
    1061}
    -
    1062
    -
    1063static bool copy_slot(qhasharr_t *tbl, int idx1, int idx2) {
    -
    1064 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    1065
    -
    1066 if (tblslots[idx1].count != 0 || tblslots[idx2].count == 0) {
    -
    1067 errno = EFAULT;
    -
    1068 return false;
    -
    1069 }
    -
    1070
    -
    1071 memcpy((void *) (&tblslots[idx1]), (void *) (&tblslots[idx2]),
    -
    1072 sizeof(qhasharr_slot_t));
    -
    1073
    -
    1074 return true;
    -
    1075}
    -
    1076
    -
    1077static bool remove_slot(qhasharr_t *tbl, int idx) {
    -
    1078 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    1079 assert(tblslots[idx].count != 0);
    -
    1080
    -
    1081 tblslots[idx].count = 0;
    -
    1082 return true;
    -
    1083}
    -
    1084
    -
    1085static bool remove_data(qhasharr_t *tbl, int idx) {
    -
    1086 qhasharr_data_t *tbldata = tbl->data;
    -
    1087 qhasharr_slot_t *tblslots = get_slots(tbl);
    -
    1088 assert(tblslots[idx].count != 0);
    -
    1089
    -
    1090 while (true) {
    -
    1091 int link = tblslots[idx].link;
    -
    1092 remove_slot(tbl, idx);
    -
    1093 tbldata->usedslots--;
    -
    1094
    -
    1095 if (link == -1)
    -
    1096 break;
    -
    1097
    -
    1098 idx = link;
    -
    1099 }
    -
    1100
    -
    1101 // decrease stored key counter
    -
    1102 tbldata->num--;
    -
    1103
    -
    1104 return true;
    -
    1105}
    -
    1106
    -
    1107#endif /* _DOXYGEN_SKIP */
    -
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition qhash.c:67
    -
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition qhash.c:263
    -
    void qhasharr_clear(qhasharr_t *tbl)
    qhasharr->clear(): Clears this table so that it contains no keys.
    Definition qhasharr.c:750
    -
    int qhasharr_size(qhasharr_t *tbl, int *maxslots, int *usedslots)
    qhasharr->size(): Returns the number of objects in this table.
    Definition qhasharr.c:727
    -
    bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
    qhasharr->put_by_obj(): ut an object into this table by key object.
    Definition qhasharr.c:344
    -
    bool qhasharr_remove_by_idx(qhasharr_t *tbl, int idx)
    qhasharr->remove_by_idx(): Remove an object from this table by index number.
    Definition qhasharr.c:591
    -
    void * qhasharr_get(qhasharr_t *tbl, const char *name, size_t *datasize)
    qhasharr->get(): Get an object from this table
    Definition qhasharr.c:444
    -
    bool qhasharr_debug(qhasharr_t *tbl, FILE *out)
    qhasharr->debug(): Print hash table for debugging purpose
    Definition qhasharr.c:780
    -
    bool qhasharr_put(qhasharr_t *tbl, const char *name, const void *data, size_t datasize)
    qhasharr->put(): Put an object into this table.
    Definition qhasharr.c:278
    -
    qhasharr_t * qhasharr(void *memory, size_t memsize)
    Initialize static hash table.
    Definition qhasharr.c:208
    -
    size_t qhasharr_calculate_memsize(int max)
    Get how much memory is needed for N slots.
    Definition qhasharr.c:179
    -
    char * qhasharr_getstr(qhasharr_t *tbl, const char *name)
    qhasharr->getstr(): Finds an object with given name and returns as string type.
    Definition qhasharr.c:464
    -
    bool qhasharr_getnext(qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx)
    qhasharr->getnext(): Get next element.
    Definition qhasharr.c:676
    -
    void * qhasharr_get_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, size_t *datasize)
    qhasharr->get_by_object(): Get an object from this table by key object
    Definition qhasharr.c:486
    -
    bool qhasharr_putstrf(qhasharr_t *tbl, const char *name, const char *format,...)
    qhasharr->putstrf(): Put a formatted string into this table.
    Definition qhasharr.c:316
    -
    void qhasharr_free(qhasharr_t *tbl)
    qhasharr->free(): De-allocate table reference object.
    Definition qhasharr.c:862
    -
    bool qhasharr_remove(qhasharr_t *tbl, const char *name)
    qhasharr->remove(): Remove an object from this table.
    Definition qhasharr.c:518
    -
    bool qhasharr_putstr(qhasharr_t *tbl, const char *name, const char *data)
    qhasharr->putstr(): Put a string into this table
    Definition qhasharr.c:297
    -
    bool qhasharr_remove_by_obj(qhasharr_t *tbl, const char *name, size_t namesize)
    qhasharr->remove_by_obj(): Remove an object from this table by key object
    Definition qhasharr.c:535
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qhasharr.c Static(array) hash-table implementation.
    +
    31  *
    +
    32  * qhasharr implements a hash-table which maps keys to values and stores into
    +
    33  * fixed size static memory like shared-memory and memory-mapped file.
    +
    34  * The creator qhasharr() initializes static memory to makes small slots in it.
    +
    35  * The default slot size factors are defined in Q_HASHARR_NAMESIZE and
    +
    36  * Q_HASHARR_DATASIZE. And they are applied at compile time.
    +
    37  *
    +
    38  * The value part of an element will be stored across several slots if it's size
    +
    39  * exceeds the slot size. But the key part of an element will be truncated if
    +
    40  * the size exceeds and it's length and more complex MD5 hash value will be
    +
    41  * stored with the key. So to look up a particular key, first we find an element
    +
    42  * which has same hash value. If the key was not truncated, we just do key
    +
    43  * comparison. But if the key was truncated because it's length exceeds, we do
    +
    44  * both md5 and key comparison(only stored size) to verify that the key is same.
    +
    45  * So please be aware of that, theoretically there is a possibility we pick
    +
    46  * wrong element in case a key exceeds the limit, has same length and MD5 hash
    +
    47  * with lookup key. But this possibility is very low and almost zero in practice.
    +
    48  *
    +
    49  * qhasharr hash-table does not provide thread-safe handling intentionally and
    +
    50  * let users determine whether to provide locking mechanism or not, depending on
    +
    51  * the use cases. When there's race conditions expected, you should provide a
    +
    52  * shared resource control using mutex or semaphore to make sure data gets
    +
    53  * updated by one instance at a time.
    +
    54  *
    +
    55  * @code
    +
    56  * [Data Structure Diagram]
    +
    57  *
    +
    58  * +--[Static Flat Memory Area]-----------------------------------------------+
    +
    59  * | +-[Header]---------+ +-[Slot 0]---+ +-[Slot 1]---+ +-[Slot N]---+ |
    +
    60  * | |Private table data| |KEY A|DATA A| |KEY B|DATA B| .... |KEY N|DATA N| |
    +
    61  * | +------------------+ +------------+ +------------+ +------------+ |
    +
    62  * +--------------------------------------------------------------------------+
    +
    63  *
    +
    64  * Below diagram shows how a big value is stored.
    +
    65  * +--[Static Flat Memory Area------------------------------------------------+
    +
    66  * | +--------+ +-[Slot 0]---+ +-[Slot 1]---+ +-[Slot 2]---+ +-[Slot 3]-----+ |
    +
    67  * | |TBL INFO| |KEY A|DATA A| |DATA A cont.| |KEY B|DATA B| |DATA A cont. | |
    +
    68  * | +--------+ +------------+ +------------+ +------------+ +--------------+ |
    +
    69  * | ^~~link~~^ ^~~~~~~~~~link~~~~~~~~~^ |
    +
    70  * +--------------------------------------------------------------------------+
    +
    71  * @endcode
    +
    72  *
    +
    73  * @code
    +
    74  * // initialize hash-table.
    +
    75  * char memory[1000 * 10];
    +
    76  * qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    +
    77  * if(tbl == NULL) return;
    +
    78  *
    +
    79  * // insert elements (key duplication does not allowed)
    +
    80  * tbl->putstr(tbl, "e1", "a");
    +
    81  * tbl->putstr(tbl, "e2", "b");
    +
    82  * tbl->putstr(tbl, "e3", "c");
    +
    83  *
    +
    84  * // debug print out
    +
    85  * tbl->debug(tbl, stdout);
    +
    86  *
    +
    87  * char *e2 = tbl->getstr(tbl, "e2");
    +
    88  * if(e2 != NULL) {
    +
    89  * printf("getstr('e2') : %s\n", e2);
    +
    90  * free(e2);
    +
    91  * }
    +
    92  *
    +
    93  * // Release reference object.
    +
    94  * tbl->free(tbl);
    +
    95  * @endcode
    +
    96  *
    +
    97  * An example for using hash table over shared memory.
    +
    98  *
    +
    99  * @code
    +
    100  * [CREATOR SIDE]
    +
    101  * int maxslots = 1000;
    +
    102  * int memsize = qhasharr_calculate_memsize(maxslots);
    +
    103  *
    +
    104  * // create shared memory
    +
    105  * int shmid = qshm_init("/tmp/some_id_file", 'q', memsize, true);
    +
    106  * if(shmid < 0) return -1; // creation failed
    +
    107  * void *memory = qshm_get(shmid);
    +
    108  *
    +
    109  * // initialize hash-table
    +
    110  * qhasharr_t *tbl = qhasharr(memory, memsize);
    +
    111  * if(hasharr == NULL) return -1;
    +
    112  *
    +
    113  * (...your codes with your own locking mechanism...)
    +
    114  *
    +
    115  * // Release reference object
    +
    116  * tbl->free(tbl);
    +
    117  *
    +
    118  * // destroy shared memory
    +
    119  * qshm_free(shmid);
    +
    120  *
    +
    121  * [USER SIDE]
    +
    122  * int shmid = qshm_getid("/tmp/some_id_file", 'q');
    +
    123  *
    +
    124  * // get shared memory
    +
    125  * void *memory = qshm_get(shmid);
    +
    126  *
    +
    127  * // map existing memory into table
    +
    128  * qhasharr_t *tbl = qhasharr(memory, 0);
    +
    129  *
    +
    130  * (...your codes with your own locking mechanism...)
    +
    131  *
    +
    132  * // Release reference object
    +
    133  * tbl->free(tbl);
    +
    134  * @endcode
    +
    135  */
    +
    136 
    +
    137 #include <stdio.h>
    +
    138 #include <stdlib.h>
    +
    139 #include <stdbool.h>
    +
    140 #include <stdint.h>
    +
    141 #include <inttypes.h>
    +
    142 #include <stdarg.h>
    +
    143 #include <string.h>
    +
    144 #include <errno.h>
    +
    145 #include <assert.h>
    +
    146 #include "qinternal.h"
    +
    147 #include "utilities/qhash.h"
    +
    148 #include "containers/qhasharr.h"
    +
    149 
    +
    150 #define COLLISION_MARK (-1)
    +
    151 #define EXTBLOCK_MARK (-2)
    +
    152 
    +
    153 #ifndef _DOXYGEN_SKIP
    +
    154 
    +
    155 static qhasharr_slot_t* get_slots(qhasharr_t *tbl);
    +
    156 static int find_avail(qhasharr_t *tbl, int startidx);
    +
    157 static int get_idx(qhasharr_t *tbl, const void *name, size_t namesize,
    +
    158  uint32_t hash);
    +
    159 static void *get_data(qhasharr_t *tbl, int idx, size_t *size);
    +
    160 static bool put_data(qhasharr_t *tbl, int idx, uint32_t hash, const void *name,
    +
    161  size_t namesize, const void *data, size_t datasize,
    +
    162  int count);
    +
    163 static bool copy_slot(qhasharr_t *tbl, int idx1, int idx2);
    +
    164 static bool remove_slot(qhasharr_t *tbl, int idx);
    +
    165 static bool remove_data(qhasharr_t *tbl, int idx);
    +
    166 
    +
    167 #endif
    +
    168 
    +
    169 /**
    +
    170  * Get how much memory is needed for N slots.
    +
    171  *
    +
    172  * @param max a number of maximum internal slots
    +
    173  *
    +
    174  * @return memory size needed
    +
    175  *
    +
    176  * @note
    +
    177  * This can be used for calculating minimum memory size for N slots.
    +
    178  */
    +
    179 size_t qhasharr_calculate_memsize(int max) {
    +
    180  size_t memsize = sizeof(qhasharr_data_t)
    +
    181  + (sizeof(qhasharr_slot_t) * (max));
    +
    182  return memsize;
    +
    183 }
    +
    184 
    +
    185 /**
    +
    186  * Initialize static hash table
    +
    187  *
    +
    188  * @param memory a pointer of data memory.
    +
    189  * @param memsize a size of data memory, 0 for using existing data.
    +
    190  *
    +
    191  * @return qhasharr_t container pointer, otherwise returns NULL.
    +
    192  * @retval errno will be set in error condition.
    +
    193  * - EINVAL : Assigned memory is too small. It must bigger enough to allocate
    +
    194  * at least 1 slot.
    +
    195  *
    +
    196  * @code
    +
    197  * // initialize hash-table with 100 slots.
    +
    198  * // A single element can take several slots.
    +
    199  * char memory[112 * 100];
    +
    200  *
    +
    201  * // Initialize new table.
    +
    202  * qhasharr_t *tbl = qhasharr(memory, sizeof(memory));
    +
    203  *
    +
    204  * // Use existing table.
    +
    205  * qhasharr_t *tbl2 = qhasharr(memory, 0);
    +
    206  * @endcode
    +
    207  */
    +
    208 qhasharr_t *qhasharr(void *memory, size_t memsize) {
    +
    209  // Structure memory.
    +
    210  qhasharr_data_t *tbldata = (qhasharr_data_t *) memory;
    +
    211 
    +
    212  // Initialize data if memsize is set or use existing data.
    +
    213  if (memsize > 0) {
    +
    214  // calculate max
    +
    215  int maxslots = (memsize - sizeof(qhasharr_data_t))
    +
    216  / sizeof(qhasharr_slot_t);
    +
    217  if (maxslots < 1 || memsize <= sizeof(qhasharr_t)) {
    +
    218  errno = EINVAL;
    +
    219  return NULL;
    +
    220  }
    +
    221 
    +
    222  // Set memory.
    +
    223  memset((void *) tbldata, 0, memsize);
    +
    224  tbldata->maxslots = maxslots;
    +
    225  tbldata->usedslots = 0;
    +
    226  tbldata->num = 0;
    +
    227  }
    +
    228 
    +
    229  // Create the table object.
    +
    230  qhasharr_t *tbl = (qhasharr_t *) malloc(sizeof(qhasharr_t));
    +
    231  if (tbl == NULL) {
    +
    232  errno = ENOMEM;
    +
    233  return NULL;
    +
    234  }
    +
    235  memset((void *) tbl, 0, sizeof(qhasharr_t));
    +
    236 
    +
    237  // assign methods
    +
    238  tbl->put = qhasharr_put;
    +
    239  tbl->putstr = qhasharr_putstr;
    +
    240  tbl->putstrf = qhasharr_putstrf;
    +
    241  tbl->put_by_obj = qhasharr_put_by_obj;
    +
    242 
    +
    243  tbl->get = qhasharr_get;
    +
    244  tbl->getstr = qhasharr_getstr;
    +
    245  tbl->get_by_obj = qhasharr_get_by_obj;
    +
    246 
    +
    247  tbl->remove = qhasharr_remove;
    +
    248  tbl->remove_by_obj = qhasharr_remove_by_obj;
    +
    249  tbl->remove_by_idx = qhasharr_remove_by_idx;
    +
    250 
    +
    251  tbl->getnext = qhasharr_getnext;
    +
    252 
    +
    253  tbl->size = qhasharr_size;
    +
    254  tbl->clear = qhasharr_clear;
    +
    255  tbl->debug = qhasharr_debug;
    +
    256 
    +
    257  tbl->free = qhasharr_free;
    +
    258 
    +
    259  tbl->data = tbldata;
    +
    260 
    +
    261  return tbl;
    +
    262 }
    +
    263 
    +
    264 /**
    +
    265  * qhasharr->put(): Put an object into this table.
    +
    266  *
    +
    267  * @param tbl qhasharr_t container pointer.
    +
    268  * @param key key string
    +
    269  * @param value value object data
    +
    270  * @param size size of value
    +
    271  *
    +
    272  * @return true if successful, otherwise returns false
    +
    273  * @retval errno will be set in error condition.
    +
    274  * - ENOBUFS : Table doesn't have enough space to store the object.
    +
    275  * - EINVAL : Invalid argument.
    +
    276  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    277  */
    +
    278 bool qhasharr_put(qhasharr_t *tbl, const char *name, const void *data,
    +
    279  size_t datasize) {
    +
    280  return qhasharr_put_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0, data,
    +
    281  datasize);
    +
    282 }
    +
    283 
    +
    284 /**
    +
    285  * qhasharr->putstr(): Put a string into this table
    +
    286  *
    +
    287  * @param tbl qhasharr_t container pointer.
    +
    288  * @param name key string.
    +
    289  * @param data value string.
    +
    290  *
    +
    291  * @return true if successful, otherwise returns false
    +
    292  * @retval errno will be set in error condition.
    +
    293  * - ENOBUFS : Table doesn't have enough space to store the object.
    +
    294  * - EINVAL : Invalid argument.
    +
    295  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    296  */
    +
    297 bool qhasharr_putstr(qhasharr_t *tbl, const char *name, const char *data) {
    +
    298  return qhasharr_put_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0,
    +
    299  data, (data) ? strlen(data) + 1 : 0);
    +
    300 }
    +
    301 
    +
    302 /**
    +
    303  * qhasharr->putstrf(): Put a formatted string into this table.
    +
    304  *
    +
    305  * @param tbl qhasharr_t container pointer.
    +
    306  * @param name key string
    +
    307  * @param format formatted string data.
    +
    308  *
    +
    309  * @return true if successful, otherwise returns false.
    +
    310  * @retval errno will be set in error condition.
    +
    311  * - ENOBUFS : Table doesn't have enough space to store the object.
    +
    312  * - ENOMEM : System memory allocation failure.
    +
    313  * - EINVAL : Invalid argument.
    +
    314  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    315  */
    +
    316 bool qhasharr_putstrf(qhasharr_t *tbl, const char *name, const char *format, ...) {
    +
    317  char *str;
    +
    318  DYNAMIC_VSPRINTF(str, format);
    +
    319  if (str == NULL) {
    +
    320  errno = ENOMEM;
    +
    321  return false;
    +
    322  }
    +
    323 
    +
    324  bool ret = qhasharr_putstr(tbl, name, str);
    +
    325  free(str);
    +
    326  return ret;
    +
    327 }
    +
    328 
    +
    329 /**
    +
    330  * qhasharr->put_by_obj(): ut an object into this table by key object.
    +
    331  *
    +
    332  * @param tbl qhasharr_t container pointer.
    +
    333  * @param name key data
    +
    334  * @param namesize size of key
    +
    335  * @param data data
    +
    336  * @param datasize size of data
    +
    337  *
    +
    338  * @return true if successful, otherwise returns false
    +
    339  * @retval errno will be set in error condition.
    +
    340  * - ENOBUFS : Table doesn't have enough space to store the object.
    +
    341  * - EINVAL : Invalid argument.
    +
    342  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    343  */
    +
    344 bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize,
    +
    345  const void *data, size_t datasize) {
    +
    346  if (tbl == NULL || name == NULL || namesize == 0 || data == NULL
    +
    347  || datasize == 0) {
    +
    348  errno = EINVAL;
    +
    349  return false;
    +
    350  }
    +
    351 
    +
    352  qhasharr_data_t *tbldata = tbl->data;
    +
    353  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    354 
    +
    355  // check full
    +
    356  if (tbldata->usedslots >= tbldata->maxslots) {
    +
    357  errno = ENOBUFS;
    +
    358  return false;
    +
    359  }
    +
    360 
    +
    361  // get hash integer
    +
    362  uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    +
    363 
    +
    364  // check, is slot empty
    +
    365  if (tblslots[hash].count == 0) { // empty slot
    +
    366  // put data
    +
    367  if (put_data(tbl, hash, hash, name, namesize, data, datasize,
    +
    368  1) == false) {
    +
    369  return false;
    +
    370  }
    +
    371  } else if (tblslots[hash].count > 0) { // same key or hash collision
    +
    372  // check same key;
    +
    373  int idx = get_idx(tbl, name, namesize, hash);
    +
    374  if (idx >= 0) { // same key
    +
    375  // remove and recall
    +
    376  qhasharr_remove_by_idx(tbl, idx);
    +
    377  return qhasharr_put_by_obj(tbl, name, namesize, data, datasize);
    +
    378  } else { // no same key but hash collision
    +
    379  // find empty slot
    +
    380  int idx = find_avail(tbl, hash);
    +
    381  if (idx < 0) {
    +
    382  errno = ENOBUFS;
    +
    383  return false;
    +
    384  }
    +
    385 
    +
    386  // put data. -1 is used for collision resolution (idx != hash);
    +
    387  if (put_data(tbl, idx, hash, name, namesize, data, datasize,
    +
    388  COLLISION_MARK) == false) {
    +
    389  return false;
    +
    390  }
    +
    391 
    +
    392  // increase counter from leading slot
    +
    393  tblslots[hash].count++;
    +
    394  }
    +
    395  } else {
    +
    396  // collision key or extended block
    +
    397 
    +
    398  // find empty slot
    +
    399  int idx = find_avail(tbl, hash + 1);
    +
    400  if (idx < 0) {
    +
    401  errno = ENOBUFS;
    +
    402  return false;
    +
    403  }
    +
    404 
    +
    405  // move the slot
    +
    406  copy_slot(tbl, idx, hash);
    +
    407  remove_slot(tbl, hash);
    +
    408 
    +
    409  // adjust the link chain
    +
    410  if (tblslots[idx].link != -1) {
    +
    411  tblslots[tblslots[idx].link].hash = idx;
    +
    412  }
    +
    413  if (tblslots[idx].count == EXTBLOCK_MARK) {
    +
    414  tblslots[tblslots[idx].hash].link = idx;
    +
    415  }
    +
    416 
    +
    417  // store data
    +
    418  if (put_data(tbl, hash, hash, name, namesize, data, datasize,
    +
    419  1) == false) {
    +
    420  return false;
    +
    421  }
    +
    422  }
    +
    423 
    +
    424  return true;
    +
    425 }
    +
    426 
    +
    427 /**
    +
    428  * qhasharr->get(): Get an object from this table
    +
    429  *
    +
    430  * @param tbl qhasharr_t container pointer.
    +
    431  * @param name key string
    +
    432  * @param datasize if not NULL, returned object size will be stored
    +
    433  *
    +
    434  * @return malloced object pointer if successful, otherwise(not found)
    +
    435  * returns NULL
    +
    436  * @retval errno will be set in error condition.
    +
    437  * - ENOENT : No such key found.
    +
    438  * - EINVAL : Invalid argument.
    +
    439  * - ENOMEM : Memory allocation failed.
    +
    440  *
    +
    441  * @note
    +
    442  * returned object must be freed after done using.
    +
    443  */
    +
    444 void *qhasharr_get(qhasharr_t *tbl, const char *name, size_t *datasize) {
    +
    445  return qhasharr_get_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0, datasize);
    +
    446 }
    +
    447 
    +
    448 /**
    +
    449  * qhasharr->getstr(): Finds an object with given name and returns as
    +
    450  * string type.
    +
    451  *
    +
    452  * @param tbl qhasharr_t container pointer.
    +
    453  * @param name key string
    +
    454  *
    +
    455  * @return string pointer if successful, otherwise(not found) returns NULL
    +
    456  * @retval errno will be set in error condition.
    +
    457  * - ENOENT : No such key found.
    +
    458  * - EINVAL : Invalid argument.
    +
    459  * - ENOMEM : Memory allocation failed.
    +
    460  *
    +
    461  * @note
    +
    462  * returned object must be freed after done using.
    +
    463  */
    +
    464 char *qhasharr_getstr(qhasharr_t *tbl, const char *name) {
    +
    465  return (char *) qhasharr_get(tbl, name, NULL);
    +
    466 }
    +
    467 
    +
    468 /**
    +
    469  * qhasharr->get_by_object(): Get an object from this table by key object
    +
    470  *
    +
    471  * @param tbl qhasharr_t container pointer.
    +
    472  * @param name key data
    +
    473  * @param namesize size of key
    +
    474  * @param datasize if not NULL, returned object size will be stored
    +
    475  *
    +
    476  * @return malloced object pointer if successful, otherwise(not found)
    +
    477  * returns NULL
    +
    478  * @retval errno will be set in error condition.
    +
    479  * - ENOENT : No such key found.
    +
    480  * - EINVAL : Invalid argument.
    +
    481  * - ENOMEM : Memory allocation failed.
    +
    482  *
    +
    483  * @note
    +
    484  * returned object must be freed after done using.
    +
    485  */
    +
    486 void *qhasharr_get_by_obj(qhasharr_t *tbl, const void *name, size_t namesize,
    +
    487  size_t *datasize) {
    +
    488  if (tbl == NULL || name == NULL || namesize == 0) {
    +
    489  errno = EINVAL;
    +
    490  return NULL;
    +
    491  }
    +
    492 
    +
    493  qhasharr_data_t *tbldata = tbl->data;
    +
    494 
    +
    495  // get hash integer
    +
    496  uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    +
    497  int idx = get_idx(tbl, name, namesize, hash);
    +
    498  if (idx < 0) {
    +
    499  errno = ENOENT;
    +
    500  return NULL;
    +
    501  }
    +
    502 
    +
    503  return get_data(tbl, idx, datasize);
    +
    504 }
    +
    505 
    +
    506 /**
    +
    507  * qhasharr->remove(): Remove an object from this table.
    +
    508  *
    +
    509  * @param tbl qhasharr_t container pointer.
    +
    510  * @param name key string
    +
    511  *
    +
    512  * @return true if successful, otherwise(not found) returns false
    +
    513  * @retval errno will be set in error condition.
    +
    514  * - ENOENT : No such key found.
    +
    515  * - EINVAL : Invald argument.
    +
    516  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    517  */
    +
    518 bool qhasharr_remove(qhasharr_t *tbl, const char *name) {
    +
    519  return qhasharr_remove_by_obj(tbl, name, (name) ? strlen(name) + 1 : 0);
    +
    520 }
    +
    521 
    +
    522 /**
    +
    523  * qhasharr->remove_by_obj(): Remove an object from this table by key object
    +
    524  *
    +
    525  * @param tbl qhasharr_t container pointer.
    +
    526  * @param name key data
    +
    527  * @param namesize size of key
    +
    528  *
    +
    529  * @return true if successful, otherwise(not found) returns false
    +
    530  * @retval errno will be set in error condition.
    +
    531  * - ENOENT : No such key found.
    +
    532  * - EINVAL : Invald argument.
    +
    533  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    534  */
    +
    535 bool qhasharr_remove_by_obj(qhasharr_t *tbl, const char *name, size_t namesize) {
    +
    536  if (tbl == NULL || name == NULL || namesize == 0) {
    +
    537  errno = EINVAL;
    +
    538  return false;
    +
    539  }
    +
    540 
    +
    541  qhasharr_data_t *tbldata = tbl->data;
    +
    542 
    +
    543  // get hash integer
    +
    544  uint32_t hash = qhashmurmur3_32(name, namesize) % tbldata->maxslots;
    +
    545  int idx = get_idx(tbl, name, namesize, hash);
    +
    546  if (idx < 0) {
    +
    547  errno = ENOENT;
    +
    548  return false;
    +
    549  }
    +
    550 
    +
    551  return qhasharr_remove_by_idx(tbl, idx);
    +
    552 }
    +
    553 
    +
    554 /**
    +
    555  * qhasharr->remove_by_idx(): Remove an object from this table by index number.
    +
    556  *
    +
    557  * @param tbl qhasharr_t container pointer.
    +
    558  * @param idx slot index number
    +
    559  *
    +
    560  * @return true if successful, otherwise(not found) returns false
    +
    561  * @retval errno will be set in error condition.
    +
    562  * - ENOENT : Index is not pointing a valid object.
    +
    563  * - EINVAL : Invald argument.
    +
    564  * - EFAULT : Unexpected error. Data structure is not constant.
    +
    565  *
    +
    566  * @code
    +
    567  * int idx = 0;
    +
    568  * qhasharr_obj_t obj;
    +
    569  * while(tbl->getnext(tbl, &obj, &idx) == true) {
    +
    570  * if (condition_to_remove) {
    +
    571  * idx--; // adjust index by -1
    +
    572  * remove_by_idx(idx);
    +
    573  * }
    +
    574  * free(obj.name);
    +
    575  * free(obj.data);
    +
    576  * }
    +
    577  * @endcode
    +
    578  *
    +
    579  * @note
    +
    580  * This function is to remove an object in getnext() traversal loop without
    +
    581  * knowing the object name but index value. When key names are longer than
    +
    582  * defined size, the keys are stored with truncation with their fingerprint,
    +
    583  * so this method provides a way to remove those keys.
    +
    584  * getnext() returns actual index + 1(pointing next slot of current finding),
    +
    585  * so you need to adjust it by -1 for the valid index number. And once you
    +
    586  * remove object by this method, rewind idx by -1 before calling getnext()
    +
    587  * because collision objects can be moved back to removed index again, so
    +
    588  * by adjusting index by -1, getnext() can continue search from the removed
    +
    589  * slot index again. Please refer an example code.
    +
    590  */
    +
    591 bool qhasharr_remove_by_idx(qhasharr_t *tbl, int idx) {
    +
    592  if (idx < 0) {
    +
    593  errno = EINVAL;
    +
    594  return false;
    +
    595  }
    +
    596 
    +
    597  qhasharr_data_t *tbldata = tbl->data;
    +
    598  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    599 
    +
    600  if (tblslots[idx].count == 1) {
    +
    601  // just remove
    +
    602  remove_data(tbl, idx);
    +
    603  } else if (tblslots[idx].count > 1) { // leading slot and has collision
    +
    604  // find the collision key
    +
    605  int idx2;
    +
    606  for (idx2 = idx + 1;; idx2++) {
    +
    607  if (idx2 >= tbldata->maxslots)
    +
    608  idx2 = 0;
    +
    609  if (idx2 == idx) {
    +
    610  errno = EFAULT;
    +
    611  return false;
    +
    612  }
    +
    613  if (tblslots[idx2].count == COLLISION_MARK
    +
    614  && tblslots[idx2].hash == tblslots[idx].hash) {
    +
    615  break;
    +
    616  }
    +
    617  }
    +
    618 
    +
    619  // move to leading slot
    +
    620  int backupcount = tblslots[idx].count;
    +
    621  remove_data(tbl, idx); // remove leading data
    +
    622  copy_slot(tbl, idx, idx2); // copy slot
    +
    623  remove_slot(tbl, idx2); // remove moved slot
    +
    624 
    +
    625  tblslots[idx].count = backupcount - 1; // adjust collision counter
    +
    626  if (tblslots[idx].link != -1) {
    +
    627  tblslots[tblslots[idx].link].hash = idx;
    +
    628  }
    +
    629 
    +
    630  } else if (tblslots[idx].count == COLLISION_MARK) { // collision key
    +
    631  // decrease counter from leading slot
    +
    632  if (tblslots[tblslots[idx].hash].count <= 1) {
    +
    633  errno = EFAULT;
    +
    634  return false;
    +
    635  }
    +
    636  tblslots[tblslots[idx].hash].count--;
    +
    637 
    +
    638  // remove data
    +
    639  remove_data(tbl, idx);
    +
    640  } else {
    +
    641  errno = ENOENT;
    +
    642  return false;
    +
    643  }
    +
    644 
    +
    645  return true;
    +
    646 }
    +
    647 
    +
    648 /**
    +
    649  * qhasharr->getnext(): Get next element.
    +
    650  *
    +
    651  * @param tbl qhasharr_t container pointer.
    +
    652  * @param idx index pointer
    +
    653  *
    +
    654  * @return key name string if successful, otherwise(end of table) returns NULL
    +
    655  * @retval errno will be set in error condition.
    +
    656  * - ENOENT : No next element.
    +
    657  * - EINVAL : Invald argument.
    +
    658  * - ENOMEM : Memory allocation failed.
    +
    659  *
    +
    660  * @code
    +
    661  * int idx = 0;
    +
    662  * qhasharr_obj_t obj;
    +
    663  * while(tbl->getnext(tbl, &obj, &idx) == true) {
    +
    664  * printf("NAMESIZE=%zu, DATASIZE=%zu\n",
    +
    665  * obj.namesize, obj.datasize);
    +
    666  * free(obj.name); // key name
    +
    667  * free(obj.data); // value data
    +
    668  * }
    +
    669  * @endcode
    +
    670  *
    +
    671  * @note
    +
    672  * Please be aware a key name will be returned with truncated length
    +
    673  * because key name gets truncated if it doesn't fit into slot size,
    +
    674  * Q_HASHARR_NAMESIZE.
    +
    675  */
    +
    676 bool qhasharr_getnext(qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx) {
    +
    677  if (tbl == NULL || obj == NULL || idx == NULL) {
    +
    678  errno = EINVAL;
    +
    679  return NULL;
    +
    680  }
    +
    681 
    +
    682  qhasharr_data_t *tbldata = tbl->data;
    +
    683  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    684 
    +
    685  for (; *idx < tbldata->maxslots; (*idx)++) {
    +
    686  if (tblslots[*idx].count == 0 || tblslots[*idx].count == EXTBLOCK_MARK) {
    +
    687  continue;
    +
    688  }
    +
    689 
    +
    690  size_t namesize = tblslots[*idx].data.pair.namesize;
    +
    691  if (namesize > Q_HASHARR_NAMESIZE)
    +
    692  namesize = Q_HASHARR_NAMESIZE;
    +
    693 
    +
    694  obj->name = malloc(namesize + 1);
    +
    695  if (obj->name == NULL) {
    +
    696  errno = ENOMEM;
    +
    697  return false;
    +
    698  }
    +
    699  memcpy(obj->name, tblslots[*idx].data.pair.name, namesize);
    +
    700  memcpy(obj->name + namesize, "", 1); // for truncated case
    +
    701  obj->namesize = namesize;
    +
    702 
    +
    703  obj->data = get_data(tbl, *idx, &obj->datasize);
    +
    704  if (obj->data == NULL) {
    +
    705  free(obj->name);
    +
    706  errno = ENOMEM;
    +
    707  return false;
    +
    708  }
    +
    709 
    +
    710  *idx += 1;
    +
    711  return true;
    +
    712  }
    +
    713 
    +
    714  errno = ENOENT;
    +
    715  return false;
    +
    716 }
    +
    717 
    +
    718 /**
    +
    719  * qhasharr->size(): Returns the number of objects in this table.
    +
    720  *
    +
    721  * @param tbl qhasharr_t container pointer.
    +
    722  * @param maxslots if not NULL, total number of slots will be stored.
    +
    723  * @param usedslots if not NULL, total number of used slots will be stored.
    +
    724  *
    +
    725  * @return a number of elements stored.
    +
    726  */
    +
    727 int qhasharr_size(qhasharr_t *tbl, int *maxslots, int *usedslots) {
    +
    728  if (tbl == NULL) {
    +
    729  errno = EINVAL;
    +
    730  return -1;
    +
    731  }
    +
    732 
    +
    733  qhasharr_data_t *tbldata = tbl->data;
    +
    734 
    +
    735  if (maxslots != NULL)
    +
    736  *maxslots = tbldata->maxslots;
    +
    737  if (usedslots != NULL)
    +
    738  *usedslots = tbldata->usedslots;
    +
    739 
    +
    740  return tbldata->num;
    +
    741 }
    +
    742 
    +
    743 /**
    +
    744  * qhasharr->clear(): Clears this table so that it contains no keys.
    +
    745  *
    +
    746  * @param tbl qhasharr_t container pointer.
    +
    747  *
    +
    748  * @return true if successful, otherwise returns false.
    +
    749  */
    +
    750 void qhasharr_clear(qhasharr_t *tbl) {
    +
    751  if (tbl == NULL) {
    +
    752  errno = EINVAL;
    +
    753  return;
    +
    754  }
    +
    755 
    +
    756  qhasharr_data_t *tbldata = tbl->data;
    +
    757  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    758 
    +
    759  if (tbldata->usedslots == 0)
    +
    760  return;
    +
    761 
    +
    762  tbldata->usedslots = 0;
    +
    763  tbldata->num = 0;
    +
    764 
    +
    765  // clear memory
    +
    766  memset((void *) tblslots, '\0',
    +
    767  (tbldata->maxslots * sizeof(qhasharr_slot_t)));
    +
    768 }
    +
    769 
    +
    770 /**
    +
    771  * qhasharr->debug(): Print hash table for debugging purpose
    +
    772  *
    +
    773  * @param tbl qhasharr_t container pointer.
    +
    774  * @param out output stream
    +
    775  *
    +
    776  * @return true if successful, otherwise returns false
    +
    777  * @retval errno will be set in error condition.
    +
    778  * - EIO : Invalid output stream.
    +
    779  */
    +
    780 bool qhasharr_debug(qhasharr_t *tbl, FILE *out) {
    +
    781  if (tbl == NULL) {
    +
    782  errno = EINVAL;
    +
    783  return false;
    +
    784  }
    +
    785 
    +
    786  if (out == NULL) {
    +
    787  errno = EIO;
    +
    788  return false;
    +
    789  }
    +
    790 
    +
    791  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    792 
    +
    793  int idx = 0;
    +
    794  qhasharr_obj_t obj;
    +
    795  while (tbl->getnext(tbl, &obj, &idx) == true) {
    +
    796  uint16_t namesize = tblslots[idx - 1].data.pair.namesize;
    +
    797  _q_textout(out, obj.name, obj.namesize, MAX_HUMANOUT);
    +
    798  fprintf(out, "%s(%d)=", (namesize > Q_HASHARR_NAMESIZE) ? "..." : "",
    +
    799  namesize);
    +
    800  _q_textout(out, obj.data, obj.datasize, MAX_HUMANOUT);
    +
    801  fprintf(out, " (%zu)\n", obj.datasize);
    +
    802 
    +
    803  free(obj.name);
    +
    804  free(obj.data);
    +
    805  }
    +
    806 
    +
    807 #ifdef BUILD_DEBUG
    +
    808  qhasharr_data_t *tbldata = tbl->data;
    +
    809  fprintf(out, "%d elements (slot %d used/%d total)\n",
    +
    810  tbldata->num, tbldata->usedslots, tbldata->maxslots);
    +
    811  for (idx = 0; idx < tbldata->maxslots; idx++) {
    +
    812  if (tblslots[idx].count == 0) continue;
    +
    813 
    +
    814  fprintf(out, "slot=%d,type=", idx);
    +
    815  if (tblslots[idx].count == EXTBLOCK_MARK) {
    +
    816  fprintf(out, "EXTEND");
    +
    817  fprintf(out, ",prev=%d", tblslots[idx].hash);
    +
    818  fprintf(out, ",next=%d", tblslots[idx].link);
    +
    819  fprintf(out, ",datasize=%d", tblslots[idx].datasize);
    +
    820  fprintf(out, ",data=");
    +
    821  _q_textout(out,
    +
    822  tblslots[idx].data.ext.data,
    +
    823  tblslots[idx].datasize,
    +
    824  MAX_HUMANOUT);
    +
    825  } else {
    +
    826  fprintf(out, "%s", (tblslots[idx].count == COLLISION_MARK)?"COLISN":"NORMAL");
    +
    827  fprintf(out, ",next=%d", tblslots[idx].link);
    +
    828  fprintf(out, ",count=%d", tblslots[idx].count);
    +
    829  fprintf(out, ",hash=%u", tblslots[idx].hash);
    +
    830  fprintf(out, ",namesize=%d", tblslots[idx].data.pair.namesize);
    +
    831  fprintf(out, ",datasize=%d", tblslots[idx].datasize);
    +
    832  fprintf(out, ",name=");
    +
    833  _q_textout(out,
    +
    834  tblslots[idx].data.pair.name,
    +
    835  (tblslots[idx].data.pair.namesize>Q_HASHARR_NAMESIZE)
    +
    836  ? Q_HASHARR_NAMESIZE
    +
    837  : tblslots[idx].data.pair.namesize,
    +
    838  MAX_HUMANOUT);
    +
    839  fprintf(out, ",data=");
    +
    840  _q_textout(out,
    +
    841  tblslots[idx].data.pair.data,
    +
    842  tblslots[idx].datasize,
    +
    843  MAX_HUMANOUT);
    +
    844  }
    +
    845  fprintf(out, "\n");
    +
    846  }
    +
    847 #endif
    +
    848 
    +
    849  return true;
    +
    850 }
    +
    851 
    +
    852 /**
    +
    853  * qhasharr->free(): De-allocate table reference object.
    +
    854  *
    +
    855  * @param tbl qhashtbl_t container pointer.
    +
    856  *
    +
    857  * @note
    +
    858  * This does not de-allocate the data memory but only the memory of
    +
    859  * qhasharr struct. User provided data memory must be de-allocated
    +
    860  * by user.
    +
    861  */
    +
    862 void qhasharr_free(qhasharr_t *tbl) {
    +
    863  free(tbl);
    +
    864 }
    +
    865 
    +
    866 #ifndef _DOXYGEN_SKIP
    +
    867 
    +
    868 static qhasharr_slot_t* get_slots(qhasharr_t *tbl) {
    +
    869  return (qhasharr_slot_t*) ((char*) (tbl->data) + sizeof(qhasharr_data_t));
    +
    870 }
    +
    871 
    +
    872 // find empty slot : return empty slow number, otherwise returns -1.
    +
    873 static int find_avail(qhasharr_t *tbl, int startidx) {
    +
    874  qhasharr_data_t *tbldata = tbl->data;
    +
    875  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    876 
    +
    877  if (startidx >= tbldata->maxslots)
    +
    878  startidx = 0;
    +
    879 
    +
    880  int idx = startidx;
    +
    881  while (true) {
    +
    882  if (tblslots[idx].count == 0)
    +
    883  return idx;
    +
    884 
    +
    885  idx++;
    +
    886  if (idx >= tbldata->maxslots)
    +
    887  idx = 0;
    +
    888  if (idx == startidx)
    +
    889  break;
    +
    890  }
    +
    891 
    +
    892  return -1;
    +
    893 }
    +
    894 
    +
    895 static int get_idx(qhasharr_t *tbl, const void *name, size_t namesize,
    +
    896  uint32_t hash) {
    +
    897  qhasharr_data_t *tbldata = tbl->data;
    +
    898  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    899 
    +
    900  if (tblslots[hash].count > 0) {
    +
    901  int count, idx;
    +
    902  for (count = 0, idx = hash; count < tblslots[hash].count;) {
    +
    903  if (tblslots[idx].hash == hash
    +
    904  && (tblslots[idx].count > 0 || tblslots[idx].count == COLLISION_MARK)) {
    +
    905  // same hash
    +
    906  count++;
    +
    907 
    +
    908  // is same key?
    +
    909  // first check key length
    +
    910  if (namesize == tblslots[idx].data.pair.namesize) {
    +
    911  if (namesize <= Q_HASHARR_NAMESIZE) {
    +
    912  // original key is stored
    +
    913  if (!memcmp(name, tblslots[idx].data.pair.name,
    +
    914  namesize)) {
    +
    915  return idx;
    +
    916  }
    +
    917  } else {
    +
    918  // key is truncated, compare MD5 also.
    +
    919  unsigned char namemd5[16];
    +
    920  qhashmd5(name, namesize, namemd5);
    +
    921  if (!memcmp(name, tblslots[idx].data.pair.name,
    +
    922  Q_HASHARR_NAMESIZE)
    +
    923  && !memcmp(namemd5,
    +
    924  tblslots[idx].data.pair.namemd5,
    +
    925  16)) {
    +
    926  return idx;
    +
    927  }
    +
    928  }
    +
    929  }
    +
    930  }
    +
    931 
    +
    932  // increase idx
    +
    933  idx++;
    +
    934  if (idx >= tbldata->maxslots)
    +
    935  idx = 0;
    +
    936 
    +
    937  // check loop
    +
    938  if (idx == hash)
    +
    939  break;
    +
    940 
    +
    941  continue;
    +
    942  }
    +
    943  }
    +
    944 
    +
    945  return -1;
    +
    946 }
    +
    947 
    +
    948 static void *get_data(qhasharr_t *tbl, int idx, size_t *size) {
    +
    949  if (idx < 0) {
    +
    950  errno = ENOENT;
    +
    951  return NULL;
    +
    952  }
    +
    953 
    +
    954  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    955 
    +
    956  int newidx;
    +
    957  size_t datasize;
    +
    958  for (newidx = idx, datasize = 0;; newidx = tblslots[newidx].link) {
    +
    959  datasize += tblslots[newidx].datasize;
    +
    960  if (tblslots[newidx].link == -1)
    +
    961  break;
    +
    962  }
    +
    963 
    +
    964  void *data, *dp;
    +
    965  if ((data = malloc(datasize)) == NULL) {
    +
    966  errno = ENOMEM;
    +
    967  return NULL;
    +
    968  }
    +
    969 
    +
    970  for (newidx = idx, dp = data;; newidx = tblslots[newidx].link) {
    +
    971  if (tblslots[newidx].count == EXTBLOCK_MARK) {
    +
    972  // extended data block
    +
    973  memcpy(dp, (void *) tblslots[newidx].data.ext.data,
    +
    974  tblslots[newidx].datasize);
    +
    975  } else {
    +
    976  // key/value pair data block
    +
    977  memcpy(dp, (void *) tblslots[newidx].data.pair.data,
    +
    978  tblslots[newidx].datasize);
    +
    979  }
    +
    980 
    +
    981  dp += tblslots[newidx].datasize;
    +
    982  if (tblslots[newidx].link == -1)
    +
    983  break;
    +
    984  }
    +
    985 
    +
    986  if (size != NULL)
    +
    987  *size = datasize;
    +
    988  return data;
    +
    989 }
    +
    990 
    +
    991 static bool put_data(qhasharr_t *tbl, int idx, uint32_t hash, const void *name,
    +
    992  size_t namesize, const void *data, size_t datasize,
    +
    993  int count) {
    +
    994  qhasharr_data_t *tbldata = tbl->data;
    +
    995  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    996 
    +
    997  assert(tblslots[idx].count == 0);
    +
    998 
    +
    999  unsigned char namemd5[16];
    +
    1000  qhashmd5(name, namesize, namemd5);
    +
    1001 
    +
    1002  // store name
    +
    1003  tblslots[idx].count = count;
    +
    1004  tblslots[idx].hash = hash;
    +
    1005  memcpy(tblslots[idx].data.pair.name, name,
    +
    1006  (namesize < Q_HASHARR_NAMESIZE) ? namesize : Q_HASHARR_NAMESIZE);
    +
    1007  memcpy((char *) tblslots[idx].data.pair.namemd5, (char *) namemd5, 16);
    +
    1008  tblslots[idx].data.pair.namesize = namesize;
    +
    1009  tblslots[idx].link = -1;
    +
    1010 
    +
    1011  // store data
    +
    1012  int newidx;
    +
    1013  size_t savesize;
    +
    1014  for (newidx = idx, savesize = 0; savesize < datasize;) {
    +
    1015  if (savesize > 0) { // find next empty slot
    +
    1016  int tmpidx = find_avail(tbl, newidx + 1);
    +
    1017  if (tmpidx < 0) {
    +
    1018  remove_data(tbl, idx);
    +
    1019  errno = ENOBUFS;
    +
    1020  return false;
    +
    1021  }
    +
    1022 
    +
    1023  // clear & set
    +
    1024  memset((void *) (&tblslots[tmpidx]), '\0', sizeof(qhasharr_slot_t));
    +
    1025 
    +
    1026  tblslots[tmpidx].count = EXTBLOCK_MARK; // extended data block
    +
    1027  tblslots[tmpidx].hash = newidx; // previous link
    +
    1028  tblslots[tmpidx].link = -1; // end block mark
    +
    1029  tblslots[tmpidx].datasize = 0;
    +
    1030  tblslots[newidx].link = tmpidx; // link chain
    +
    1031 
    +
    1032  newidx = tmpidx;
    +
    1033  }
    +
    1034 
    +
    1035  // copy data
    +
    1036  size_t copysize = datasize - savesize;
    +
    1037  if (tblslots[newidx].count == EXTBLOCK_MARK) {
    +
    1038  // extended value
    +
    1039  if (copysize > sizeof(struct Q_HASHARR_SLOT_EXT)) {
    +
    1040  copysize = sizeof(struct Q_HASHARR_SLOT_EXT);
    +
    1041  }
    +
    1042  memcpy(tblslots[newidx].data.ext.data, data + savesize, copysize);
    +
    1043  } else {
    +
    1044  // first slot
    +
    1045  if (copysize > Q_HASHARR_DATASIZE) {
    +
    1046  copysize = Q_HASHARR_DATASIZE;
    +
    1047  }
    +
    1048  memcpy(tblslots[newidx].data.pair.data, data + savesize, copysize);
    +
    1049 
    +
    1050  // increase stored key counter
    +
    1051  tbldata->num++;
    +
    1052  }
    +
    1053  tblslots[newidx].datasize = copysize;
    +
    1054  savesize += copysize;
    +
    1055 
    +
    1056  // increase used slot counter
    +
    1057  tbldata->usedslots++;
    +
    1058  }
    +
    1059 
    +
    1060  return true;
    +
    1061 }
    +
    1062 
    +
    1063 static bool copy_slot(qhasharr_t *tbl, int idx1, int idx2) {
    +
    1064  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    1065 
    +
    1066  if (tblslots[idx1].count != 0 || tblslots[idx2].count == 0) {
    +
    1067  errno = EFAULT;
    +
    1068  return false;
    +
    1069  }
    +
    1070 
    +
    1071  memcpy((void *) (&tblslots[idx1]), (void *) (&tblslots[idx2]),
    +
    1072  sizeof(qhasharr_slot_t));
    +
    1073 
    +
    1074  return true;
    +
    1075 }
    +
    1076 
    +
    1077 static bool remove_slot(qhasharr_t *tbl, int idx) {
    +
    1078  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    1079  assert(tblslots[idx].count != 0);
    +
    1080 
    +
    1081  tblslots[idx].count = 0;
    +
    1082  return true;
    +
    1083 }
    +
    1084 
    +
    1085 static bool remove_data(qhasharr_t *tbl, int idx) {
    +
    1086  qhasharr_data_t *tbldata = tbl->data;
    +
    1087  qhasharr_slot_t *tblslots = get_slots(tbl);
    +
    1088  assert(tblslots[idx].count != 0);
    +
    1089 
    +
    1090  while (true) {
    +
    1091  int link = tblslots[idx].link;
    +
    1092  remove_slot(tbl, idx);
    +
    1093  tbldata->usedslots--;
    +
    1094 
    +
    1095  if (link == -1)
    +
    1096  break;
    +
    1097 
    +
    1098  idx = link;
    +
    1099  }
    +
    1100 
    +
    1101  // decrease stored key counter
    +
    1102  tbldata->num--;
    +
    1103 
    +
    1104  return true;
    +
    1105 }
    +
    1106 
    +
    1107 #endif /* _DOXYGEN_SKIP */
    +
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition: qhash.c:67
    +
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition: qhash.c:263
    +
    void qhasharr_clear(qhasharr_t *tbl)
    qhasharr->clear(): Clears this table so that it contains no keys.
    Definition: qhasharr.c:750
    +
    int qhasharr_size(qhasharr_t *tbl, int *maxslots, int *usedslots)
    qhasharr->size(): Returns the number of objects in this table.
    Definition: qhasharr.c:727
    +
    void * qhasharr_get_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, size_t *datasize)
    qhasharr->get_by_object(): Get an object from this table by key object
    Definition: qhasharr.c:486
    +
    bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
    qhasharr->put_by_obj(): ut an object into this table by key object.
    Definition: qhasharr.c:344
    +
    bool qhasharr_remove_by_idx(qhasharr_t *tbl, int idx)
    qhasharr->remove_by_idx(): Remove an object from this table by index number.
    Definition: qhasharr.c:591
    +
    void * qhasharr_get(qhasharr_t *tbl, const char *name, size_t *datasize)
    qhasharr->get(): Get an object from this table
    Definition: qhasharr.c:444
    +
    bool qhasharr_debug(qhasharr_t *tbl, FILE *out)
    qhasharr->debug(): Print hash table for debugging purpose
    Definition: qhasharr.c:780
    +
    bool qhasharr_put(qhasharr_t *tbl, const char *name, const void *data, size_t datasize)
    qhasharr->put(): Put an object into this table.
    Definition: qhasharr.c:278
    +
    size_t qhasharr_calculate_memsize(int max)
    Get how much memory is needed for N slots.
    Definition: qhasharr.c:179
    +
    char * qhasharr_getstr(qhasharr_t *tbl, const char *name)
    qhasharr->getstr(): Finds an object with given name and returns as string type.
    Definition: qhasharr.c:464
    +
    bool qhasharr_getnext(qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx)
    qhasharr->getnext(): Get next element.
    Definition: qhasharr.c:676
    +
    bool qhasharr_putstrf(qhasharr_t *tbl, const char *name, const char *format,...)
    qhasharr->putstrf(): Put a formatted string into this table.
    Definition: qhasharr.c:316
    +
    qhasharr_t * qhasharr(void *memory, size_t memsize)
    Initialize static hash table.
    Definition: qhasharr.c:208
    +
    void qhasharr_free(qhasharr_t *tbl)
    qhasharr->free(): De-allocate table reference object.
    Definition: qhasharr.c:862
    +
    bool qhasharr_remove(qhasharr_t *tbl, const char *name)
    qhasharr->remove(): Remove an object from this table.
    Definition: qhasharr.c:518
    +
    bool qhasharr_putstr(qhasharr_t *tbl, const char *name, const char *data)
    qhasharr->putstr(): Put a string into this table
    Definition: qhasharr.c:297
    +
    bool qhasharr_remove_by_obj(qhasharr_t *tbl, const char *name, size_t namesize)
    qhasharr->remove_by_obj(): Remove an object from this table by key object
    Definition: qhasharr.c:535
    diff --git a/doc/html/qhashtbl_8c.html b/doc/html/qhashtbl_8c.html index 3628ec45..a2cb3c90 100644 --- a/doc/html/qhashtbl_8c.html +++ b/doc/html/qhashtbl_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qhashtbl.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qhashtbl.c File Reference
    +
    +
    qhashtbl.c File Reference
    @@ -71,65 +71,65 @@

    Go to the source code of this file.

    -

    +

    Macros

    #define DEFAULT_INDEX_RANGE   (1000)
     
    - - - - + + + - + - + - + - + - - - - - - + + + + + + - + - + - + - + - + - + - + - + - +

    +

    Functions

    qhashtbl_t * qhashtbl (size_t range, int options)
     Initialize hash table.
     
    qhashtbl_t * qhashtbl (size_t range, int options)
     Initialize hash table. More...
     
    bool qhashtbl_put (qhashtbl_t *tbl, const char *name, const void *data, size_t size)
     qhashtbl->put(): Put an object into this table.
     qhashtbl->put(): Put an object into this table. More...
     
    bool qhashtbl_putstr (qhashtbl_t *tbl, const char *name, const char *str)
     qhashtbl->putstr(): Put a string into this table.
     qhashtbl->putstr(): Put a string into this table. More...
     
    bool qhashtbl_putstrf (qhashtbl_t *tbl, const char *name, const char *format,...)
     qhashtbl->putstrf(): Put a formatted string into this table.
     qhashtbl->putstrf(): Put a formatted string into this table. More...
     
    bool qhashtbl_putint (qhashtbl_t *tbl, const char *name, const int64_t num)
     qhashtbl->putint(): Put a integer into this table as string type.
     qhashtbl->putint(): Put a integer into this table as string type. More...
     
    void * qhashtbl_get (qhashtbl_t *tbl, const char *name, size_t *size, bool newmem)
     qhashtbl->get(): Get an object from this table.
     
    char * qhashtbl_getstr (qhashtbl_t *tbl, const char *name, const bool newmem)
     qhashtbl->getstr(): Finds an object and returns as string type.
     
    void * qhashtbl_get (qhashtbl_t *tbl, const char *name, size_t *size, bool newmem)
     qhashtbl->get(): Get an object from this table. More...
     
    char * qhashtbl_getstr (qhashtbl_t *tbl, const char *name, const bool newmem)
     qhashtbl->getstr(): Finds an object and returns as string type. More...
     
    int64_t qhashtbl_getint (qhashtbl_t *tbl, const char *name)
     qhashtbl->getint(): Finds an object with given name and returns as integer type.
     qhashtbl->getint(): Finds an object with given name and returns as integer type. More...
     
    bool qhashtbl_remove (qhashtbl_t *tbl, const char *name)
     qhashtbl->remove(): Remove an object from this table.
     qhashtbl->remove(): Remove an object from this table. More...
     
    bool qhashtbl_getnext (qhashtbl_t *tbl, qhashtbl_obj_t *obj, const bool newmem)
     qhashtbl->getnext(): Get next element.
     qhashtbl->getnext(): Get next element. More...
     
    size_t qhashtbl_size (qhashtbl_t *tbl)
     qhashtbl->size(): Returns the number of keys in this hashtable.
     qhashtbl->size(): Returns the number of keys in this hashtable. More...
     
    void qhashtbl_clear (qhashtbl_t *tbl)
     qhashtbl->clear(): Clears this hashtable so that it contains no keys.
     qhashtbl->clear(): Clears this hashtable so that it contains no keys. More...
     
    bool qhashtbl_debug (qhashtbl_t *tbl, FILE *out)
     qhashtbl->debug(): Print hash table for debugging purpose
     qhashtbl->debug(): Print hash table for debugging purpose More...
     
    void qhashtbl_lock (qhashtbl_t *tbl)
     qhashtbl->lock(): Enter critical section.
     qhashtbl->lock(): Enter critical section. More...
     
    void qhashtbl_unlock (qhashtbl_t *tbl)
     qhashtbl->unlock(): Leave critical section.
     qhashtbl->unlock(): Leave critical section. More...
     
    void qhashtbl_free (qhashtbl_t *tbl)
     qhashtbl->free(): De-allocate hash table
     qhashtbl->free(): De-allocate hash table More...
     

    Detailed Description

    Hash-table container implementation.

    -

    qhashtbl implements a hash table, which maps keys to values. Key is a unique string and value is any non-null object. The creator qhashtbl() has a parameter that affect its performance: initial hash range. The hash range is the number of slots(pointers) in the hash table. in the case of a hash collision, a single slots stores multiple elements using linked-list structure, which must be searched sequentially. So lower range than the number of elements decreases the space overhead but increases the number of hash collisions and consequently it increases the time cost to look up an element.

    +

    qhashtbl implements a hash table, which maps keys to values. Key is a unique string and value is any non-null object. The creator qhashtbl() has a parameter that affect its performance: initial hash range. The hash range is the number of slots(pointers) in the hash table. in the case of a hash collision, a single slots stores multiple elements using linked-list structure, which must be searched sequentially. So lower range than the number of elements decreases the space overhead but increases the number of hash collisions and consequently it increases the time cost to look up an element.

    [Internal Structure Example for 10-slot hash table]
    RANGE NAMED-OBJECT-LIST
    @@ -149,7 +149,7 @@
    // objects which can be stored. You can put as many as you want but if
    // this range is too small, hash conflict will happen and fetch time will
    // slightly increase.
    -
    qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    +
    qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    // put objects into table.
    tbl->put(tbl, "sample1", "binary", 6);
    @@ -169,12 +169,12 @@
    // release table
    tbl->free(tbl);
    -
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition qhashtbl.c:127
    +
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition: qhashtbl.c:127

    Definition in file qhashtbl.c.

    Macro Definition Documentation

    - -

    ◆ DEFAULT_INDEX_RANGE

    + +

    ◆ DEFAULT_INDEX_RANGE

    @@ -191,14 +191,14 @@

    Function Documentation

    - -

    ◆ qhashtbl()

    + +

    ◆ qhashtbl()

    - + @@ -236,10 +236,10 @@

    // create a hash-table.
    -
    +
    qhashtbl_t *basic_hashtbl = qhashtbl(0, 0);
    // create a large hash-table for millions of keys with thread-safe option.
    -
    qhashtbl_t *small_hashtbl = qhashtbl(1000000, QHASHTBL_THREADSAFE);
    +
    qhashtbl_t *small_hashtbl = qhashtbl(1000000, QHASHTBL_THREADSAFE);
    Note
    Setting the right range is a magic. In practice, pick a value between (total keys / 3) ~ (total keys * 2). Available options:
    • QHASHTBL_THREADSAFE - make it thread-safe.
    @@ -249,8 +249,8 @@

    -

    ◆ qhashtbl_put()

    + +

    ◆ qhashtbl_put()

    @@ -313,8 +313,8 @@

    -

    ◆ qhashtbl_putstr()

    + +

    ◆ qhashtbl_putstr()

    @@ -370,8 +370,8 @@

    -

    ◆ qhashtbl_putstrf()

    + +

    ◆ qhashtbl_putstrf()

    @@ -433,8 +433,8 @@

    -

    ◆ qhashtbl_putint()

    + +

    ◆ qhashtbl_putint()

    @@ -491,14 +491,14 @@

    -

    ◆ qhashtbl_get()

    + +

    ◆ qhashtbl_get()

    qhashtbl_t * qhashtbl qhashtbl_t* qhashtbl ( size_t  range,
    - + @@ -551,7 +551,7 @@

    +
    qhashtbl_t *tbl = qhashtbl(0, 0);
    (...codes...)
    // with newmem flag unset
    @@ -568,14 +568,14 @@

    -

    ◆ qhashtbl_getstr()

    + +

    ◆ qhashtbl_getstr()

    void * qhashtbl_get void* qhashtbl_get ( qhashtbl_t *  tbl,
    - + @@ -627,8 +627,8 @@

    -

    ◆ qhashtbl_getint()

    + +

    ◆ qhashtbl_getint()

    @@ -678,8 +678,8 @@

    -

    ◆ qhashtbl_remove()

    + +

    ◆ qhashtbl_remove()

    @@ -728,8 +728,8 @@

    -

    ◆ qhashtbl_getnext()

    + +

    ◆ qhashtbl_getnext()

    @@ -781,7 +781,7 @@

    +
    qhashtbl_t *tbl = qhashtbl(0, 0);
    (...add data into list...)
    qhashtbl_obj_t obj;
    @@ -799,8 +799,8 @@

    -

    ◆ qhashtbl_size()

    + +

    ◆ qhashtbl_size()

    @@ -828,8 +828,8 @@

    -

    ◆ qhashtbl_clear()

    + +

    ◆ qhashtbl_clear()

    @@ -856,8 +856,8 @@

    -

    ◆ qhashtbl_debug()

    + +

    ◆ qhashtbl_debug()

    @@ -905,8 +905,8 @@

    -

    ◆ qhashtbl_lock()

    + +

    ◆ qhashtbl_lock()

    @@ -936,8 +936,8 @@

    -

    ◆ qhashtbl_unlock()

    + +

    ◆ qhashtbl_unlock()

    @@ -965,8 +965,8 @@

    -

    ◆ qhashtbl_free()

    + +

    ◆ qhashtbl_free()

    @@ -999,7 +999,7 @@

    diff --git a/doc/html/qhashtbl_8c.js b/doc/html/qhashtbl_8c.js index ddf9722c..71d10d09 100644 --- a/doc/html/qhashtbl_8c.js +++ b/doc/html/qhashtbl_8c.js @@ -1,13 +1,13 @@ var qhashtbl_8c = [ [ "DEFAULT_INDEX_RANGE", "qhashtbl_8c.html#a3a9cf947d23f63b5a86e4ab6f815c4fd", null ], - [ "qhashtbl", "qhashtbl_8c.html#a7a2314dcb8381b4599e8050becad2268", null ], + [ "qhashtbl", "qhashtbl_8c.html#acce512d557a3c776d474bfc977f34045", null ], [ "qhashtbl_put", "qhashtbl_8c.html#aa156cfc5dbc28dd5c082dbf0d354fd5f", null ], [ "qhashtbl_putstr", "qhashtbl_8c.html#a8832c9892ab3a4057dcf72589f00685a", null ], [ "qhashtbl_putstrf", "qhashtbl_8c.html#abfea47ba4675f272f0ecaf5041987f8b", null ], [ "qhashtbl_putint", "qhashtbl_8c.html#ae6a51c2268d057d6de84c0e3b661ba60", null ], - [ "qhashtbl_get", "qhashtbl_8c.html#adb6639e5a035c6c82d558f75d094bb8a", null ], - [ "qhashtbl_getstr", "qhashtbl_8c.html#acd67a76b916bb5cd4b0aae2d37b23c9a", null ], + [ "qhashtbl_get", "qhashtbl_8c.html#a4ebd413a2ffc36b4a72692aba402c4fd", null ], + [ "qhashtbl_getstr", "qhashtbl_8c.html#ad0b71ffa5b24fd9b14ddfede6e872859", null ], [ "qhashtbl_getint", "qhashtbl_8c.html#a28ecdb8684c5f059ae179d78e4d4b02a", null ], [ "qhashtbl_remove", "qhashtbl_8c.html#a0e16876f85af045651b4e14a98760ee6", null ], [ "qhashtbl_getnext", "qhashtbl_8c.html#a5c30813d4a8dbf3dbb23c394d7799393", null ], diff --git a/doc/html/qhashtbl_8c_source.html b/doc/html/qhashtbl_8c_source.html index 2110588c..51db718b 100644 --- a/doc/html/qhashtbl_8c_source.html +++ b/doc/html/qhashtbl_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qhashtbl.c Source File @@ -20,8 +20,8 @@

    char * qhashtbl_getstr char* qhashtbl_getstr ( qhashtbl_t *  tbl,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,755 +52,756 @@
    -
    qhashtbl.c
    +
    +
    qhashtbl.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qhashtbl.c Hash-table container implementation.
    -
    31 *
    -
    32 * qhashtbl implements a hash table, which maps keys to values. Key is a unique
    -
    33 * string and value is any non-null object. The creator qhashtbl() has a
    -
    34 * parameter that affect its performance: initial hash range. The hash range
    -
    35 * is the number of slots(pointers) in the hash table. in the case of a hash
    -
    36 * collision, a single slots stores multiple elements using linked-list
    -
    37 * structure, which must be searched sequentially. So lower range than the
    -
    38 * number of elements decreases the space overhead but increases the number of
    -
    39 * hash collisions and consequently it increases the time cost to look up an
    -
    40 * element.
    -
    41 *
    -
    42 * @code
    -
    43 * [Internal Structure Example for 10-slot hash table]
    -
    44 *
    -
    45 * RANGE NAMED-OBJECT-LIST
    -
    46 * ===== =================
    -
    47 * [ 0 ] -> [hash=320,key3=value] -> [hash=210,key5=value] -> [hash=110,...]
    -
    48 * [ 1 ] -> [hash=1,key1=value]
    -
    49 * [ 2 ]
    -
    50 * [ 3 ] -> [hash=873,key4=value]
    -
    51 * [ 4 ] -> [hash=2674,key11=value] -> [hash=214,key5=value]
    -
    52 * [ 5 ] -> [hash=8545,key10=value]
    -
    53 * [ 6 ] -> [hash=9226,key9=value]
    -
    54 * [ 7 ]
    -
    55 * [ 8 ] -> [hash=8,key6=value] -> [hash=88,key8=value]
    -
    56 * [ 9 ] -> [hash=12439,key7=value]
    -
    57 * @endcode
    -
    58 *
    -
    59 * @code
    -
    60 * // create a hash-table with 10 hash-index range.
    -
    61 * // Please be aware, the hash-index range 10 does not mean the number of
    -
    62 * // objects which can be stored. You can put as many as you want but if
    -
    63 * // this range is too small, hash conflict will happen and fetch time will
    -
    64 * // slightly increase.
    -
    65 * qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    -
    66 *
    -
    67 * // put objects into table.
    -
    68 * tbl->put(tbl, "sample1", "binary", 6);
    -
    69 * tbl->putstr(tbl, "sample2", "string");
    -
    70 * tbl->putint(tbl, "sample3", 1);
    -
    71 *
    -
    72 * // debug print out
    -
    73 * tbl->debug(tbl, stdout, true);
    -
    74 *
    -
    75 * // get objects
    -
    76 * void *sample1 = tbl->get(tbl, "sample1", &size, true);
    -
    77 * char *sample2 = tbl->getstr(tbl, "sample2", false);
    -
    78 * int sample3 = tbl->getint(tbl, "sample3");
    -
    79 *
    -
    80 * // sample1 is memalloced
    -
    81 * free(sample1);
    -
    82 *
    -
    83 * // release table
    -
    84 * tbl->free(tbl);
    -
    85 * @endcode
    -
    86 */
    -
    87
    -
    88#include <stdio.h>
    -
    89#include <stdlib.h>
    -
    90#include <stdbool.h>
    -
    91#include <stdint.h>
    -
    92#include <inttypes.h>
    -
    93#include <stdarg.h>
    -
    94#include <string.h>
    -
    95#include <assert.h>
    -
    96#include <errno.h>
    -
    97#include "qinternal.h"
    -
    98#include "utilities/qhash.h"
    -
    99#include "containers/qhashtbl.h"
    -
    100
    -
    101#define DEFAULT_INDEX_RANGE (1000) /*!< default value of hash-index range */
    -
    102
    -
    103/**
    -
    104 * Initialize hash table.
    -
    105 *
    -
    106 * @param range initial size of index range. Value of 0 will use default value, DEFAULT_INDEX_RANGE;
    -
    107 * @param options combination of initialization options.
    -
    108 *
    -
    109 * @return a pointer of malloced qhashtbl_t, otherwise returns NULL.
    -
    110 * @retval errno will be set in error condition.
    -
    111 * - ENOMEM : Memory allocation failure.
    -
    112 *
    -
    113 * @code
    -
    114 * // create a hash-table.
    -
    115 * qhashtbl_t *basic_hashtbl = qhashtbl(0, 0);
    -
    116 *
    -
    117 * // create a large hash-table for millions of keys with thread-safe option.
    -
    118 * qhashtbl_t *small_hashtbl = qhashtbl(1000000, QHASHTBL_THREADSAFE);
    -
    119 * @endcode
    -
    120 *
    -
    121 * @note
    -
    122 * Setting the right range is a magic.
    -
    123 * In practice, pick a value between (total keys / 3) ~ (total keys * 2).
    -
    124 * Available options:
    -
    125 * - QHASHTBL_THREADSAFE - make it thread-safe.
    -
    126 */
    -
    127qhashtbl_t *qhashtbl(size_t range, int options) {
    -
    128 if (range == 0) {
    -
    129 range = DEFAULT_INDEX_RANGE;
    -
    130 }
    -
    131
    -
    132 qhashtbl_t *tbl = (qhashtbl_t *) calloc(1, sizeof(qhashtbl_t));
    -
    133 if (tbl == NULL)
    -
    134 goto malloc_failure;
    -
    135
    -
    136 // allocate table space
    -
    137 tbl->slots = (qhashtbl_obj_t **) calloc(range, sizeof(qhashtbl_obj_t *));
    -
    138 if (tbl->slots == NULL)
    -
    139 goto malloc_failure;
    -
    140
    -
    141 // handle options.
    -
    142 if (options & QHASHTBL_THREADSAFE) {
    -
    143 Q_MUTEX_NEW(tbl->qmutex, true);
    -
    144 if (tbl->qmutex == NULL)
    -
    145 goto malloc_failure;
    -
    146 }
    -
    147
    -
    148 // assign methods
    -
    149 tbl->put = qhashtbl_put;
    -
    150 tbl->putstr = qhashtbl_putstr;
    -
    151 tbl->putstrf = qhashtbl_putstrf;
    -
    152 tbl->putint = qhashtbl_putint;
    -
    153
    -
    154 tbl->get = qhashtbl_get;
    -
    155 tbl->getstr = qhashtbl_getstr;
    -
    156 tbl->getint = qhashtbl_getint;
    -
    157
    -
    158 tbl->remove = qhashtbl_remove;
    -
    159
    -
    160 tbl->getnext = qhashtbl_getnext;
    -
    161
    -
    162 tbl->size = qhashtbl_size;
    -
    163 tbl->clear = qhashtbl_clear;
    -
    164 tbl->debug = qhashtbl_debug;
    -
    165
    -
    166 tbl->lock = qhashtbl_lock;
    -
    167 tbl->unlock = qhashtbl_unlock;
    -
    168
    -
    169 tbl->free = qhashtbl_free;
    -
    170
    -
    171 // set table range.
    -
    172 tbl->range = range;
    -
    173
    -
    174 return tbl;
    -
    175
    -
    176 malloc_failure:
    -
    177 errno = ENOMEM;
    -
    178 if (tbl) {
    -
    179 assert(tbl->qmutex == NULL);
    -
    180 qhashtbl_free(tbl);
    -
    181 }
    -
    182 return NULL;
    -
    183}
    -
    184
    -
    185/**
    -
    186 * qhashtbl->put(): Put an object into this table.
    -
    187 *
    -
    188 * @param tbl qhashtbl_t container pointer.
    -
    189 * @param name key name
    -
    190 * @param data data object
    -
    191 * @param size size of data object
    -
    192 *
    -
    193 * @return true if successful, otherwise returns false
    -
    194 * @retval errno will be set in error condition.
    -
    195 * - EINVAL : Invalid argument.
    -
    196 * - ENOMEM : Memory allocation failure.
    -
    197 */
    -
    198bool qhashtbl_put(qhashtbl_t *tbl, const char *name, const void *data,
    -
    199 size_t size) {
    -
    200 if (name == NULL || data == NULL) {
    -
    201 errno = EINVAL;
    -
    202 return false;
    -
    203 }
    -
    204
    -
    205 // get hash integer
    -
    206 uint32_t hash = qhashmurmur3_32(name, strlen(name));
    -
    207 int idx = hash % tbl->range;
    -
    208
    -
    209 qhashtbl_lock(tbl);
    -
    210
    -
    211 // find existence key
    -
    212 qhashtbl_obj_t *obj;
    -
    213 for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    -
    214 if (obj->hash == hash && !strcmp(obj->name, name)) {
    -
    215 break;
    -
    216 }
    -
    217 }
    -
    218
    -
    219 // duplicate object
    -
    220 char *dupname = strdup(name);
    -
    221 void *dupdata = malloc(size);
    -
    222 if (dupname == NULL || dupdata == NULL) {
    -
    223 free(dupname);
    -
    224 free(dupdata);
    -
    225 qhashtbl_unlock(tbl);
    -
    226 errno = ENOMEM;
    -
    227 return false;
    -
    228 }
    -
    229 memcpy(dupdata, data, size);
    -
    230
    -
    231 // put into table
    -
    232 if (obj == NULL) {
    -
    233 // insert
    -
    234 obj = (qhashtbl_obj_t *) calloc(1, sizeof(qhashtbl_obj_t));
    -
    235 if (obj == NULL) {
    -
    236 free(dupname);
    -
    237 free(dupdata);
    -
    238 qhashtbl_unlock(tbl);
    -
    239 errno = ENOMEM;
    -
    240 return false;
    -
    241 }
    -
    242
    -
    243 if (tbl->slots[idx] != NULL) {
    -
    244 // insert at the beginning
    -
    245 obj->next = tbl->slots[idx];
    -
    246 }
    -
    247 tbl->slots[idx] = obj;
    -
    248
    -
    249 // increase counter
    -
    250 tbl->num++;
    -
    251 } else {
    -
    252 // replace
    -
    253 free(obj->name);
    -
    254 free(obj->data);
    -
    255 }
    -
    256
    -
    257 // set data
    -
    258 obj->hash = hash;
    -
    259 obj->name = dupname;
    -
    260 obj->data = dupdata;
    -
    261 obj->size = size;
    -
    262
    -
    263 qhashtbl_unlock(tbl);
    -
    264 return true;
    -
    265}
    -
    266
    -
    267/**
    -
    268 * qhashtbl->putstr(): Put a string into this table.
    -
    269 *
    -
    270 * @param tbl qhashtbl_t container pointer.
    -
    271 * @param name key name.
    -
    272 * @param str string data.
    -
    273 *
    -
    274 * @return true if successful, otherwise returns false.
    -
    275 * @retval errno will be set in error condition.
    -
    276 * - EINVAL : Invalid argument.
    -
    277 * - ENOMEM : Memory allocation failure.
    -
    278 */
    -
    279bool qhashtbl_putstr(qhashtbl_t *tbl, const char *name, const char *str) {
    -
    280 return qhashtbl_put(tbl, name, str, (str != NULL) ? (strlen(str) + 1) : 0);
    -
    281}
    -
    282
    -
    283/**
    -
    284 * qhashtbl->putstrf(): Put a formatted string into this table.
    -
    285 *
    -
    286 * @param tbl qhashtbl_t container pointer.
    -
    287 * @param name key name.
    -
    288 * @param format formatted string data.
    -
    289 *
    -
    290 * @return true if successful, otherwise returns false.
    -
    291 * @retval errno will be set in error condition.
    -
    292 * - EINVAL : Invalid argument.
    -
    293 * - ENOMEM : Memory allocation failure.
    -
    294 */
    -
    295bool qhashtbl_putstrf(qhashtbl_t *tbl, const char *name, const char *format, ...) {
    -
    296 char *str;
    -
    297 DYNAMIC_VSPRINTF(str, format);
    -
    298 if (str == NULL) {
    -
    299 errno = ENOMEM;
    -
    300 return false;
    -
    301 }
    -
    302
    -
    303 bool ret = qhashtbl_putstr(tbl, name, str);
    -
    304 free(str);
    -
    305 return ret;
    -
    306}
    -
    307
    -
    308/**
    -
    309 * qhashtbl->putint(): Put a integer into this table as string type.
    -
    310 *
    -
    311 * @param tbl qhashtbl_t container pointer.
    -
    312 * @param name key name.
    -
    313 * @param num integer data.
    -
    314 *
    -
    315 * @return true if successful, otherwise returns false.
    -
    316 * @retval errno will be set in error condition.
    -
    317 * - EINVAL : Invalid argument.
    -
    318 * - ENOMEM : Memory allocation failure.
    -
    319 *
    -
    320 * @note
    -
    321 * The integer will be converted to a string object and stored as string object.
    -
    322 */
    -
    323bool qhashtbl_putint(qhashtbl_t *tbl, const char *name, const int64_t num) {
    -
    324 char str[20 + 1];
    -
    325 snprintf(str, sizeof(str), "%"PRId64, num);
    -
    326 return qhashtbl_putstr(tbl, name, str);
    -
    327}
    -
    328
    -
    329/**
    -
    330 * qhashtbl->get(): Get an object from this table.
    -
    331 *
    -
    332 * @param tbl qhashtbl_t container pointer.
    -
    333 * @param name key name.
    -
    334 * @param size if not NULL, oject size will be stored.
    -
    335 * @param newmem whether or not to allocate memory for the data.
    -
    336 *
    -
    337 * @return a pointer of data if the key is found, otherwise returns NULL.
    -
    338 * @retval errno will be set in error condition.
    -
    339 * - ENOENT : No such key found.
    -
    340 * - EINVAL : Invalid argument.
    -
    341 * - ENOMEM : Memory allocation failure.
    -
    342 *
    -
    343 * @code
    -
    344 * qhashtbl_t *tbl = qhashtbl(0, 0);
    -
    345 * (...codes...)
    -
    346 *
    -
    347 * // with newmem flag unset
    -
    348 * size_t size;
    -
    349 * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false);
    -
    350 *
    -
    351 * // with newmem flag set
    -
    352 * size_t size;
    -
    353 * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true);
    -
    354 * free(data);
    -
    355 * @endcode
    -
    356 *
    -
    357 * @note
    -
    358 * If newmem flag is set, returned data will be malloced and should be
    -
    359 * deallocated by user. Otherwise returned pointer will point internal buffer
    -
    360 * directly and should not be de-allocated by user. In thread-safe mode,
    -
    361 * newmem flag must be set to true always.
    -
    362 */
    -
    363void *qhashtbl_get(qhashtbl_t *tbl, const char *name, size_t *size, bool newmem) {
    -
    364 if (name == NULL) {
    -
    365 errno = EINVAL;
    -
    366 return NULL;
    -
    367 }
    -
    368
    -
    369 uint32_t hash = qhashmurmur3_32(name, strlen(name));
    -
    370 int idx = hash % tbl->range;
    -
    371
    -
    372 qhashtbl_lock(tbl);
    -
    373
    -
    374 // find key
    -
    375 qhashtbl_obj_t *obj;
    -
    376 for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    -
    377 if (obj->hash == hash && !strcmp(obj->name, name)) {
    -
    378 break;
    -
    379 }
    -
    380 }
    -
    381
    -
    382 void *data = NULL;
    -
    383 if (obj != NULL) {
    -
    384 if (newmem == false) {
    -
    385 data = obj->data;
    -
    386 } else {
    -
    387 data = malloc(obj->size);
    -
    388 if (data == NULL) {
    -
    389 errno = ENOMEM;
    -
    390 return NULL;
    -
    391 }
    -
    392 memcpy(data, obj->data, obj->size);
    -
    393 }
    -
    394 if (size != NULL && data != NULL)
    -
    395 *size = obj->size;
    -
    396 }
    -
    397
    -
    398 qhashtbl_unlock(tbl);
    -
    399
    -
    400 if (data == NULL)
    -
    401 errno = ENOENT;
    -
    402 return data;
    -
    403}
    -
    404
    -
    405/**
    -
    406 * qhashtbl->getstr(): Finds an object and returns as string type.
    -
    407 *
    -
    408 * @param tbl qhashtbl_t container pointer.
    -
    409 * @param name key name
    -
    410 * @param newmem whether or not to allocate memory for the data.
    -
    411 *
    -
    412 * @return a pointer of data if the key is found, otherwise returns NULL.
    -
    413 * @retval errno will be set in error condition.
    -
    414 * - ENOENT : No such key found.
    -
    415 * - EINVAL : Invalid argument.
    -
    416 * - ENOMEM : Memory allocation failure.
    -
    417 *
    -
    418 * @note
    -
    419 * If newmem flag is set, returned data will be malloced and should be
    -
    420 * deallocated by user.
    -
    421 */
    -
    422char *qhashtbl_getstr(qhashtbl_t *tbl, const char *name, const bool newmem) {
    -
    423 return qhashtbl_get(tbl, name, NULL, newmem);
    -
    424}
    -
    425
    -
    426/**
    -
    427 * qhashtbl->getint(): Finds an object with given name and returns as
    -
    428 * integer type.
    -
    429 *
    -
    430 * @param tbl qhashtbl_t container pointer.
    -
    431 * @param name key name
    -
    432 *
    -
    433 * @return value integer if successful, otherwise(not found) returns 0
    -
    434 * @retval errno will be set in error condition.
    -
    435 * - ENOENT : No such key found.
    -
    436 * - EINVAL : Invalid argument.
    -
    437 * - ENOMEM : Memory allocation failure.
    -
    438 */
    -
    439int64_t qhashtbl_getint(qhashtbl_t *tbl, const char *name) {
    -
    440 int64_t num = 0;
    -
    441 char *str = qhashtbl_getstr(tbl, name, true);
    -
    442 if (str != NULL) {
    -
    443 num = atoll(str);
    -
    444 free(str);
    -
    445 }
    -
    446
    -
    447 return num;
    -
    448}
    -
    449
    -
    450/**
    -
    451 * qhashtbl->remove(): Remove an object from this table.
    -
    452 *
    -
    453 * @param tbl qhashtbl_t container pointer.
    -
    454 * @param name key name
    -
    455 *
    -
    456 * @return true if successful, otherwise(not found) returns false
    -
    457 * @retval errno will be set in error condition.
    -
    458 * - ENOENT : No such key found.
    -
    459 * - EINVAL : Invalid argument.
    -
    460 */
    -
    461bool qhashtbl_remove(qhashtbl_t *tbl, const char *name) {
    -
    462 if (name == NULL) {
    -
    463 errno = EINVAL;
    -
    464 return false;
    -
    465 }
    -
    466
    -
    467 qhashtbl_lock(tbl);
    -
    468
    -
    469 uint32_t hash = qhashmurmur3_32(name, strlen(name));
    -
    470 int idx = hash % tbl->range;
    -
    471
    -
    472 // find key
    -
    473 bool found = false;
    -
    474 qhashtbl_obj_t *prev = NULL;
    -
    475 qhashtbl_obj_t *obj;
    -
    476 for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    -
    477 if (obj->hash == hash && !strcmp(obj->name, name)) {
    -
    478 // adjust link
    -
    479 if (prev == NULL)
    -
    480 tbl->slots[idx] = obj->next;
    -
    481 else
    -
    482 prev->next = obj->next;
    -
    483
    -
    484 // remove
    -
    485 free(obj->name);
    -
    486 free(obj->data);
    -
    487 free(obj);
    -
    488
    -
    489 found = true;
    -
    490 tbl->num--;
    -
    491 break;
    -
    492 }
    -
    493
    -
    494 prev = obj;
    -
    495 }
    -
    496
    -
    497 qhashtbl_unlock(tbl);
    -
    498
    -
    499 if (found == false)
    -
    500 errno = ENOENT;
    -
    501
    -
    502 return found;
    -
    503}
    -
    504
    -
    505/**
    -
    506 * qhashtbl->getnext(): Get next element.
    -
    507 *
    -
    508 * @param tbl qhashtbl_t container pointer.
    -
    509 * @param obj found data will be stored in this object
    -
    510 * @param newmem whether or not to allocate memory for the data.
    -
    511 *
    -
    512 * @return true if found otherwise returns false
    -
    513 * @retval errno will be set in error condition.
    -
    514 * - ENOENT : No next element.
    -
    515 * - EINVAL : Invalid argument.
    -
    516 * - ENOMEM : Memory allocation failure.
    -
    517 *
    -
    518 * @code
    -
    519 * qhashtbl_t *tbl = qhashtbl(0, 0);
    -
    520 * (...add data into list...)
    -
    521 *
    -
    522 * qhashtbl_obj_t obj;
    -
    523 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    524 * tbl->lock(tbl); // lock it when thread condition is expected
    -
    525 * while(tbl->getnext(tbl, &obj, false) == true) { // newmem is false
    -
    526 * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    -
    527 * obj.name, (char*)obj.data, obj.size);
    -
    528 * // do free obj.name and obj.data if newmem was set to true;
    -
    529 * }
    -
    530 * tbl->unlock(tbl);
    -
    531 * @endcode
    -
    532 *
    -
    533 * @note
    -
    534 * locking must be provided on user code when all element scan must be
    -
    535 * guaranteed where multiple threads concurrently update the table.
    -
    536 * It's ok not to lock the table on the user code even in thread condition,
    -
    537 * when concurreny is importand and all element scan in a path doesn't need
    -
    538 * to be guaranteed. In this case, new data inserted during the traversal
    -
    539 * will be show up in this scan or next scan. Make sure newmem flag is set
    -
    540 * if deletion is expected during the scan.
    -
    541 * Object obj should be initialized with 0 by using memset() before first call.
    -
    542 */
    -
    543bool qhashtbl_getnext(qhashtbl_t *tbl, qhashtbl_obj_t *obj, const bool newmem) {
    -
    544 if (obj == NULL) {
    -
    545 errno = EINVAL;
    -
    546 return NULL;
    -
    547 }
    -
    548
    -
    549 qhashtbl_lock(tbl);
    -
    550
    -
    551 bool found = false;
    -
    552
    -
    553 qhashtbl_obj_t *cursor = NULL;
    -
    554 int idx = 0;
    -
    555 if (obj->name != NULL) {
    -
    556 idx = (obj->hash % tbl->range) + 1;
    -
    557 cursor = obj->next;
    -
    558 }
    -
    559
    -
    560 if (cursor != NULL) {
    -
    561 // has link
    -
    562 found = true;
    -
    563 } else {
    -
    564 // search from next index
    -
    565 for (; idx < tbl->range; idx++) {
    -
    566 if (tbl->slots[idx] != NULL) {
    -
    567 cursor = tbl->slots[idx];
    -
    568 found = true;
    -
    569 break;
    -
    570 }
    -
    571 }
    -
    572 }
    -
    573
    -
    574 if (cursor != NULL) {
    -
    575 if (newmem == true) {
    -
    576 obj->name = strdup(cursor->name);
    -
    577 obj->data = malloc(cursor->size);
    -
    578 if (obj->name == NULL || obj->data == NULL) {
    -
    579 DEBUG("getnext(): Unable to allocate memory.");
    -
    580 free(obj->name);
    -
    581 free(obj->data);
    -
    582 qhashtbl_unlock(tbl);
    -
    583 errno = ENOMEM;
    -
    584 return false;
    -
    585 }
    -
    586 memcpy(obj->data, cursor->data, cursor->size);
    -
    587 obj->size = cursor->size;
    -
    588 } else {
    -
    589 obj->name = cursor->name;
    -
    590 obj->data = cursor->data;
    -
    591 }
    -
    592 obj->hash = cursor->hash;
    -
    593 obj->size = cursor->size;
    -
    594 obj->next = cursor->next;
    -
    595
    -
    596 }
    -
    597
    -
    598 qhashtbl_unlock(tbl);
    -
    599
    -
    600 if (found == false)
    -
    601 errno = ENOENT;
    -
    602
    -
    603 return found;
    -
    604}
    -
    605
    -
    606/**
    -
    607 * qhashtbl->size(): Returns the number of keys in this hashtable.
    -
    608 *
    -
    609 * @param tbl qhashtbl_t container pointer.
    -
    610 *
    -
    611 * @return number of elements stored
    -
    612 */
    -
    613size_t qhashtbl_size(qhashtbl_t *tbl) {
    -
    614 return tbl->num;
    -
    615}
    -
    616
    -
    617/**
    -
    618 * qhashtbl->clear(): Clears this hashtable so that it contains no keys.
    -
    619 *
    -
    620 * @param tbl qhashtbl_t container pointer.
    -
    621 */
    -
    622void qhashtbl_clear(qhashtbl_t *tbl) {
    -
    623 qhashtbl_lock(tbl);
    -
    624 int idx;
    -
    625 for (idx = 0; idx < tbl->range && tbl->num > 0; idx++) {
    -
    626 if (tbl->slots[idx] == NULL)
    -
    627 continue;
    -
    628 qhashtbl_obj_t *obj = tbl->slots[idx];
    -
    629 tbl->slots[idx] = NULL;
    -
    630 while (obj != NULL) {
    -
    631 qhashtbl_obj_t *next = obj->next;
    -
    632 free(obj->name);
    -
    633 free(obj->data);
    -
    634 free(obj);
    -
    635 obj = next;
    -
    636
    -
    637 tbl->num--;
    -
    638 }
    -
    639 }
    -
    640
    -
    641 qhashtbl_unlock(tbl);
    -
    642}
    -
    643
    -
    644/**
    -
    645 * qhashtbl->debug(): Print hash table for debugging purpose
    -
    646 *
    -
    647 * @param tbl qhashtbl_t container pointer.
    -
    648 * @param out output stream
    -
    649 *
    -
    650 * @return true if successful, otherwise returns false.
    -
    651 * @retval errno will be set in error condition.
    -
    652 * - EIO : Invalid output stream.
    -
    653 */
    -
    654bool qhashtbl_debug(qhashtbl_t *tbl, FILE *out) {
    -
    655 if (out == NULL) {
    -
    656 errno = EIO;
    -
    657 return false;
    -
    658 }
    -
    659
    -
    660 qhashtbl_obj_t obj;
    -
    661 memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call
    -
    662 qhashtbl_lock(tbl);
    -
    663 while (tbl->getnext(tbl, &obj, false) == true) {
    -
    664 fprintf(out, "%s=", obj.name);
    -
    665 _q_textout(out, obj.data, obj.size, MAX_HUMANOUT);
    -
    666 fprintf(out, " (%zu, %08x)\n", obj.size, obj.hash);
    -
    667 }
    -
    668 qhashtbl_unlock(tbl);
    -
    669
    -
    670 return true;
    -
    671}
    -
    672
    -
    673/**
    -
    674 * qhashtbl->lock(): Enter critical section.
    -
    675 *
    -
    676 * @param tbl qhashtbl_t container pointer.
    -
    677 *
    -
    678 * @note
    -
    679 * From user side, normally locking operation is only needed when traverse
    -
    680 * all elements using qhashtbl->getnext().
    -
    681 *
    -
    682 * @note
    -
    683 * This operation will do nothing if QHASHTBL_THREADSAFE option was not
    -
    684 * given at the initialization time.
    -
    685 */
    -
    686void qhashtbl_lock(qhashtbl_t *tbl) {
    -
    687 Q_MUTEX_ENTER(tbl->qmutex);
    -
    688}
    -
    689
    -
    690/**
    -
    691 * qhashtbl->unlock(): Leave critical section.
    -
    692 *
    -
    693 * @param tbl qhashtbl_t container pointer.
    -
    694 *
    -
    695 * @note
    -
    696 * This operation will do nothing if QHASHTBL_THREADSAFE option was not
    -
    697 * given at the initialization time.
    -
    698 */
    -
    699void qhashtbl_unlock(qhashtbl_t *tbl) {
    -
    700 Q_MUTEX_LEAVE(tbl->qmutex);
    -
    701}
    -
    702
    -
    703/**
    -
    704 * qhashtbl->free(): De-allocate hash table
    -
    705 *
    -
    706 * @param tbl qhashtbl_t container pointer.
    -
    707 */
    -
    708void qhashtbl_free(qhashtbl_t *tbl) {
    -
    709 qhashtbl_lock(tbl);
    -
    710 qhashtbl_clear(tbl);
    -
    711 free(tbl->slots);
    -
    712 qhashtbl_unlock(tbl);
    -
    713 Q_MUTEX_DESTROY(tbl->qmutex);
    -
    714 free(tbl);
    -
    715}
    -
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition qhash.c:263
    -
    bool qhashtbl_remove(qhashtbl_t *tbl, const char *name)
    qhashtbl->remove(): Remove an object from this table.
    Definition qhashtbl.c:461
    -
    bool qhashtbl_debug(qhashtbl_t *tbl, FILE *out)
    qhashtbl->debug(): Print hash table for debugging purpose
    Definition qhashtbl.c:654
    -
    void qhashtbl_clear(qhashtbl_t *tbl)
    qhashtbl->clear(): Clears this hashtable so that it contains no keys.
    Definition qhashtbl.c:622
    -
    int64_t qhashtbl_getint(qhashtbl_t *tbl, const char *name)
    qhashtbl->getint(): Finds an object with given name and returns as integer type.
    Definition qhashtbl.c:439
    -
    void qhashtbl_unlock(qhashtbl_t *tbl)
    qhashtbl->unlock(): Leave critical section.
    Definition qhashtbl.c:699
    -
    #define DEFAULT_INDEX_RANGE
    Definition qhashtbl.c:101
    -
    void qhashtbl_free(qhashtbl_t *tbl)
    qhashtbl->free(): De-allocate hash table
    Definition qhashtbl.c:708
    -
    bool qhashtbl_getnext(qhashtbl_t *tbl, qhashtbl_obj_t *obj, const bool newmem)
    qhashtbl->getnext(): Get next element.
    Definition qhashtbl.c:543
    -
    size_t qhashtbl_size(qhashtbl_t *tbl)
    qhashtbl->size(): Returns the number of keys in this hashtable.
    Definition qhashtbl.c:613
    -
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition qhashtbl.c:127
    -
    void qhashtbl_lock(qhashtbl_t *tbl)
    qhashtbl->lock(): Enter critical section.
    Definition qhashtbl.c:686
    -
    bool qhashtbl_putstr(qhashtbl_t *tbl, const char *name, const char *str)
    qhashtbl->putstr(): Put a string into this table.
    Definition qhashtbl.c:279
    -
    bool qhashtbl_put(qhashtbl_t *tbl, const char *name, const void *data, size_t size)
    qhashtbl->put(): Put an object into this table.
    Definition qhashtbl.c:198
    -
    bool qhashtbl_putstrf(qhashtbl_t *tbl, const char *name, const char *format,...)
    qhashtbl->putstrf(): Put a formatted string into this table.
    Definition qhashtbl.c:295
    -
    char * qhashtbl_getstr(qhashtbl_t *tbl, const char *name, const bool newmem)
    qhashtbl->getstr(): Finds an object and returns as string type.
    Definition qhashtbl.c:422
    -
    void * qhashtbl_get(qhashtbl_t *tbl, const char *name, size_t *size, bool newmem)
    qhashtbl->get(): Get an object from this table.
    Definition qhashtbl.c:363
    -
    bool qhashtbl_putint(qhashtbl_t *tbl, const char *name, const int64_t num)
    qhashtbl->putint(): Put a integer into this table as string type.
    Definition qhashtbl.c:323
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qhashtbl.c Hash-table container implementation.
    +
    31  *
    +
    32  * qhashtbl implements a hash table, which maps keys to values. Key is a unique
    +
    33  * string and value is any non-null object. The creator qhashtbl() has a
    +
    34  * parameter that affect its performance: initial hash range. The hash range
    +
    35  * is the number of slots(pointers) in the hash table. in the case of a hash
    +
    36  * collision, a single slots stores multiple elements using linked-list
    +
    37  * structure, which must be searched sequentially. So lower range than the
    +
    38  * number of elements decreases the space overhead but increases the number of
    +
    39  * hash collisions and consequently it increases the time cost to look up an
    +
    40  * element.
    +
    41  *
    +
    42  * @code
    +
    43  * [Internal Structure Example for 10-slot hash table]
    +
    44  *
    +
    45  * RANGE NAMED-OBJECT-LIST
    +
    46  * ===== =================
    +
    47  * [ 0 ] -> [hash=320,key3=value] -> [hash=210,key5=value] -> [hash=110,...]
    +
    48  * [ 1 ] -> [hash=1,key1=value]
    +
    49  * [ 2 ]
    +
    50  * [ 3 ] -> [hash=873,key4=value]
    +
    51  * [ 4 ] -> [hash=2674,key11=value] -> [hash=214,key5=value]
    +
    52  * [ 5 ] -> [hash=8545,key10=value]
    +
    53  * [ 6 ] -> [hash=9226,key9=value]
    +
    54  * [ 7 ]
    +
    55  * [ 8 ] -> [hash=8,key6=value] -> [hash=88,key8=value]
    +
    56  * [ 9 ] -> [hash=12439,key7=value]
    +
    57  * @endcode
    +
    58  *
    +
    59  * @code
    +
    60  * // create a hash-table with 10 hash-index range.
    +
    61  * // Please be aware, the hash-index range 10 does not mean the number of
    +
    62  * // objects which can be stored. You can put as many as you want but if
    +
    63  * // this range is too small, hash conflict will happen and fetch time will
    +
    64  * // slightly increase.
    +
    65  * qhashtbl_t *tbl = qhashtbl(0, QHASHTBL_THREADSAFE);
    +
    66  *
    +
    67  * // put objects into table.
    +
    68  * tbl->put(tbl, "sample1", "binary", 6);
    +
    69  * tbl->putstr(tbl, "sample2", "string");
    +
    70  * tbl->putint(tbl, "sample3", 1);
    +
    71  *
    +
    72  * // debug print out
    +
    73  * tbl->debug(tbl, stdout, true);
    +
    74  *
    +
    75  * // get objects
    +
    76  * void *sample1 = tbl->get(tbl, "sample1", &size, true);
    +
    77  * char *sample2 = tbl->getstr(tbl, "sample2", false);
    +
    78  * int sample3 = tbl->getint(tbl, "sample3");
    +
    79  *
    +
    80  * // sample1 is memalloced
    +
    81  * free(sample1);
    +
    82  *
    +
    83  * // release table
    +
    84  * tbl->free(tbl);
    +
    85  * @endcode
    +
    86  */
    +
    87 
    +
    88 #include <stdio.h>
    +
    89 #include <stdlib.h>
    +
    90 #include <stdbool.h>
    +
    91 #include <stdint.h>
    +
    92 #include <inttypes.h>
    +
    93 #include <stdarg.h>
    +
    94 #include <string.h>
    +
    95 #include <assert.h>
    +
    96 #include <errno.h>
    +
    97 #include "qinternal.h"
    +
    98 #include "utilities/qhash.h"
    +
    99 #include "containers/qhashtbl.h"
    +
    100 
    +
    101 #define DEFAULT_INDEX_RANGE (1000) /*!< default value of hash-index range */
    +
    102 
    +
    103 /**
    +
    104  * Initialize hash table.
    +
    105  *
    +
    106  * @param range initial size of index range. Value of 0 will use default value, DEFAULT_INDEX_RANGE;
    +
    107  * @param options combination of initialization options.
    +
    108  *
    +
    109  * @return a pointer of malloced qhashtbl_t, otherwise returns NULL.
    +
    110  * @retval errno will be set in error condition.
    +
    111  * - ENOMEM : Memory allocation failure.
    +
    112  *
    +
    113  * @code
    +
    114  * // create a hash-table.
    +
    115  * qhashtbl_t *basic_hashtbl = qhashtbl(0, 0);
    +
    116  *
    +
    117  * // create a large hash-table for millions of keys with thread-safe option.
    +
    118  * qhashtbl_t *small_hashtbl = qhashtbl(1000000, QHASHTBL_THREADSAFE);
    +
    119  * @endcode
    +
    120  *
    +
    121  * @note
    +
    122  * Setting the right range is a magic.
    +
    123  * In practice, pick a value between (total keys / 3) ~ (total keys * 2).
    +
    124  * Available options:
    +
    125  * - QHASHTBL_THREADSAFE - make it thread-safe.
    +
    126  */
    +
    127 qhashtbl_t *qhashtbl(size_t range, int options) {
    +
    128  if (range == 0) {
    +
    129  range = DEFAULT_INDEX_RANGE;
    +
    130  }
    +
    131 
    +
    132  qhashtbl_t *tbl = (qhashtbl_t *) calloc(1, sizeof(qhashtbl_t));
    +
    133  if (tbl == NULL)
    +
    134  goto malloc_failure;
    +
    135 
    +
    136  // allocate table space
    +
    137  tbl->slots = (qhashtbl_obj_t **) calloc(range, sizeof(qhashtbl_obj_t *));
    +
    138  if (tbl->slots == NULL)
    +
    139  goto malloc_failure;
    +
    140 
    +
    141  // handle options.
    +
    142  if (options & QHASHTBL_THREADSAFE) {
    +
    143  Q_MUTEX_NEW(tbl->qmutex, true);
    +
    144  if (tbl->qmutex == NULL)
    +
    145  goto malloc_failure;
    +
    146  }
    +
    147 
    +
    148  // assign methods
    +
    149  tbl->put = qhashtbl_put;
    +
    150  tbl->putstr = qhashtbl_putstr;
    +
    151  tbl->putstrf = qhashtbl_putstrf;
    +
    152  tbl->putint = qhashtbl_putint;
    +
    153 
    +
    154  tbl->get = qhashtbl_get;
    +
    155  tbl->getstr = qhashtbl_getstr;
    +
    156  tbl->getint = qhashtbl_getint;
    +
    157 
    +
    158  tbl->remove = qhashtbl_remove;
    +
    159 
    +
    160  tbl->getnext = qhashtbl_getnext;
    +
    161 
    +
    162  tbl->size = qhashtbl_size;
    +
    163  tbl->clear = qhashtbl_clear;
    +
    164  tbl->debug = qhashtbl_debug;
    +
    165 
    +
    166  tbl->lock = qhashtbl_lock;
    +
    167  tbl->unlock = qhashtbl_unlock;
    +
    168 
    +
    169  tbl->free = qhashtbl_free;
    +
    170 
    +
    171  // set table range.
    +
    172  tbl->range = range;
    +
    173 
    +
    174  return tbl;
    +
    175 
    +
    176  malloc_failure:
    +
    177  errno = ENOMEM;
    +
    178  if (tbl) {
    +
    179  assert(tbl->qmutex == NULL);
    +
    180  qhashtbl_free(tbl);
    +
    181  }
    +
    182  return NULL;
    +
    183 }
    +
    184 
    +
    185 /**
    +
    186  * qhashtbl->put(): Put an object into this table.
    +
    187  *
    +
    188  * @param tbl qhashtbl_t container pointer.
    +
    189  * @param name key name
    +
    190  * @param data data object
    +
    191  * @param size size of data object
    +
    192  *
    +
    193  * @return true if successful, otherwise returns false
    +
    194  * @retval errno will be set in error condition.
    +
    195  * - EINVAL : Invalid argument.
    +
    196  * - ENOMEM : Memory allocation failure.
    +
    197  */
    +
    198 bool qhashtbl_put(qhashtbl_t *tbl, const char *name, const void *data,
    +
    199  size_t size) {
    +
    200  if (name == NULL || data == NULL) {
    +
    201  errno = EINVAL;
    +
    202  return false;
    +
    203  }
    +
    204 
    +
    205  // get hash integer
    +
    206  uint32_t hash = qhashmurmur3_32(name, strlen(name));
    +
    207  int idx = hash % tbl->range;
    +
    208 
    +
    209  qhashtbl_lock(tbl);
    +
    210 
    +
    211  // find existence key
    +
    212  qhashtbl_obj_t *obj;
    +
    213  for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    +
    214  if (obj->hash == hash && !strcmp(obj->name, name)) {
    +
    215  break;
    +
    216  }
    +
    217  }
    +
    218 
    +
    219  // duplicate object
    +
    220  char *dupname = strdup(name);
    +
    221  void *dupdata = malloc(size);
    +
    222  if (dupname == NULL || dupdata == NULL) {
    +
    223  free(dupname);
    +
    224  free(dupdata);
    +
    225  qhashtbl_unlock(tbl);
    +
    226  errno = ENOMEM;
    +
    227  return false;
    +
    228  }
    +
    229  memcpy(dupdata, data, size);
    +
    230 
    +
    231  // put into table
    +
    232  if (obj == NULL) {
    +
    233  // insert
    +
    234  obj = (qhashtbl_obj_t *) calloc(1, sizeof(qhashtbl_obj_t));
    +
    235  if (obj == NULL) {
    +
    236  free(dupname);
    +
    237  free(dupdata);
    +
    238  qhashtbl_unlock(tbl);
    +
    239  errno = ENOMEM;
    +
    240  return false;
    +
    241  }
    +
    242 
    +
    243  if (tbl->slots[idx] != NULL) {
    +
    244  // insert at the beginning
    +
    245  obj->next = tbl->slots[idx];
    +
    246  }
    +
    247  tbl->slots[idx] = obj;
    +
    248 
    +
    249  // increase counter
    +
    250  tbl->num++;
    +
    251  } else {
    +
    252  // replace
    +
    253  free(obj->name);
    +
    254  free(obj->data);
    +
    255  }
    +
    256 
    +
    257  // set data
    +
    258  obj->hash = hash;
    +
    259  obj->name = dupname;
    +
    260  obj->data = dupdata;
    +
    261  obj->size = size;
    +
    262 
    +
    263  qhashtbl_unlock(tbl);
    +
    264  return true;
    +
    265 }
    +
    266 
    +
    267 /**
    +
    268  * qhashtbl->putstr(): Put a string into this table.
    +
    269  *
    +
    270  * @param tbl qhashtbl_t container pointer.
    +
    271  * @param name key name.
    +
    272  * @param str string data.
    +
    273  *
    +
    274  * @return true if successful, otherwise returns false.
    +
    275  * @retval errno will be set in error condition.
    +
    276  * - EINVAL : Invalid argument.
    +
    277  * - ENOMEM : Memory allocation failure.
    +
    278  */
    +
    279 bool qhashtbl_putstr(qhashtbl_t *tbl, const char *name, const char *str) {
    +
    280  return qhashtbl_put(tbl, name, str, (str != NULL) ? (strlen(str) + 1) : 0);
    +
    281 }
    +
    282 
    +
    283 /**
    +
    284  * qhashtbl->putstrf(): Put a formatted string into this table.
    +
    285  *
    +
    286  * @param tbl qhashtbl_t container pointer.
    +
    287  * @param name key name.
    +
    288  * @param format formatted string data.
    +
    289  *
    +
    290  * @return true if successful, otherwise returns false.
    +
    291  * @retval errno will be set in error condition.
    +
    292  * - EINVAL : Invalid argument.
    +
    293  * - ENOMEM : Memory allocation failure.
    +
    294  */
    +
    295 bool qhashtbl_putstrf(qhashtbl_t *tbl, const char *name, const char *format, ...) {
    +
    296  char *str;
    +
    297  DYNAMIC_VSPRINTF(str, format);
    +
    298  if (str == NULL) {
    +
    299  errno = ENOMEM;
    +
    300  return false;
    +
    301  }
    +
    302 
    +
    303  bool ret = qhashtbl_putstr(tbl, name, str);
    +
    304  free(str);
    +
    305  return ret;
    +
    306 }
    +
    307 
    +
    308 /**
    +
    309  * qhashtbl->putint(): Put a integer into this table as string type.
    +
    310  *
    +
    311  * @param tbl qhashtbl_t container pointer.
    +
    312  * @param name key name.
    +
    313  * @param num integer data.
    +
    314  *
    +
    315  * @return true if successful, otherwise returns false.
    +
    316  * @retval errno will be set in error condition.
    +
    317  * - EINVAL : Invalid argument.
    +
    318  * - ENOMEM : Memory allocation failure.
    +
    319  *
    +
    320  * @note
    +
    321  * The integer will be converted to a string object and stored as string object.
    +
    322  */
    +
    323 bool qhashtbl_putint(qhashtbl_t *tbl, const char *name, const int64_t num) {
    +
    324  char str[20 + 1];
    +
    325  snprintf(str, sizeof(str), "%"PRId64, num);
    +
    326  return qhashtbl_putstr(tbl, name, str);
    +
    327 }
    +
    328 
    +
    329 /**
    +
    330  * qhashtbl->get(): Get an object from this table.
    +
    331  *
    +
    332  * @param tbl qhashtbl_t container pointer.
    +
    333  * @param name key name.
    +
    334  * @param size if not NULL, oject size will be stored.
    +
    335  * @param newmem whether or not to allocate memory for the data.
    +
    336  *
    +
    337  * @return a pointer of data if the key is found, otherwise returns NULL.
    +
    338  * @retval errno will be set in error condition.
    +
    339  * - ENOENT : No such key found.
    +
    340  * - EINVAL : Invalid argument.
    +
    341  * - ENOMEM : Memory allocation failure.
    +
    342  *
    +
    343  * @code
    +
    344  * qhashtbl_t *tbl = qhashtbl(0, 0);
    +
    345  * (...codes...)
    +
    346  *
    +
    347  * // with newmem flag unset
    +
    348  * size_t size;
    +
    349  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false);
    +
    350  *
    +
    351  * // with newmem flag set
    +
    352  * size_t size;
    +
    353  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true);
    +
    354  * free(data);
    +
    355  * @endcode
    +
    356  *
    +
    357  * @note
    +
    358  * If newmem flag is set, returned data will be malloced and should be
    +
    359  * deallocated by user. Otherwise returned pointer will point internal buffer
    +
    360  * directly and should not be de-allocated by user. In thread-safe mode,
    +
    361  * newmem flag must be set to true always.
    +
    362  */
    +
    363 void *qhashtbl_get(qhashtbl_t *tbl, const char *name, size_t *size, bool newmem) {
    +
    364  if (name == NULL) {
    +
    365  errno = EINVAL;
    +
    366  return NULL;
    +
    367  }
    +
    368 
    +
    369  uint32_t hash = qhashmurmur3_32(name, strlen(name));
    +
    370  int idx = hash % tbl->range;
    +
    371 
    +
    372  qhashtbl_lock(tbl);
    +
    373 
    +
    374  // find key
    +
    375  qhashtbl_obj_t *obj;
    +
    376  for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    +
    377  if (obj->hash == hash && !strcmp(obj->name, name)) {
    +
    378  break;
    +
    379  }
    +
    380  }
    +
    381 
    +
    382  void *data = NULL;
    +
    383  if (obj != NULL) {
    +
    384  if (newmem == false) {
    +
    385  data = obj->data;
    +
    386  } else {
    +
    387  data = malloc(obj->size);
    +
    388  if (data == NULL) {
    +
    389  errno = ENOMEM;
    +
    390  return NULL;
    +
    391  }
    +
    392  memcpy(data, obj->data, obj->size);
    +
    393  }
    +
    394  if (size != NULL && data != NULL)
    +
    395  *size = obj->size;
    +
    396  }
    +
    397 
    +
    398  qhashtbl_unlock(tbl);
    +
    399 
    +
    400  if (data == NULL)
    +
    401  errno = ENOENT;
    +
    402  return data;
    +
    403 }
    +
    404 
    +
    405 /**
    +
    406  * qhashtbl->getstr(): Finds an object and returns as string type.
    +
    407  *
    +
    408  * @param tbl qhashtbl_t container pointer.
    +
    409  * @param name key name
    +
    410  * @param newmem whether or not to allocate memory for the data.
    +
    411  *
    +
    412  * @return a pointer of data if the key is found, otherwise returns NULL.
    +
    413  * @retval errno will be set in error condition.
    +
    414  * - ENOENT : No such key found.
    +
    415  * - EINVAL : Invalid argument.
    +
    416  * - ENOMEM : Memory allocation failure.
    +
    417  *
    +
    418  * @note
    +
    419  * If newmem flag is set, returned data will be malloced and should be
    +
    420  * deallocated by user.
    +
    421  */
    +
    422 char *qhashtbl_getstr(qhashtbl_t *tbl, const char *name, const bool newmem) {
    +
    423  return qhashtbl_get(tbl, name, NULL, newmem);
    +
    424 }
    +
    425 
    +
    426 /**
    +
    427  * qhashtbl->getint(): Finds an object with given name and returns as
    +
    428  * integer type.
    +
    429  *
    +
    430  * @param tbl qhashtbl_t container pointer.
    +
    431  * @param name key name
    +
    432  *
    +
    433  * @return value integer if successful, otherwise(not found) returns 0
    +
    434  * @retval errno will be set in error condition.
    +
    435  * - ENOENT : No such key found.
    +
    436  * - EINVAL : Invalid argument.
    +
    437  * - ENOMEM : Memory allocation failure.
    +
    438  */
    +
    439 int64_t qhashtbl_getint(qhashtbl_t *tbl, const char *name) {
    +
    440  int64_t num = 0;
    +
    441  char *str = qhashtbl_getstr(tbl, name, true);
    +
    442  if (str != NULL) {
    +
    443  num = atoll(str);
    +
    444  free(str);
    +
    445  }
    +
    446 
    +
    447  return num;
    +
    448 }
    +
    449 
    +
    450 /**
    +
    451  * qhashtbl->remove(): Remove an object from this table.
    +
    452  *
    +
    453  * @param tbl qhashtbl_t container pointer.
    +
    454  * @param name key name
    +
    455  *
    +
    456  * @return true if successful, otherwise(not found) returns false
    +
    457  * @retval errno will be set in error condition.
    +
    458  * - ENOENT : No such key found.
    +
    459  * - EINVAL : Invalid argument.
    +
    460  */
    +
    461 bool qhashtbl_remove(qhashtbl_t *tbl, const char *name) {
    +
    462  if (name == NULL) {
    +
    463  errno = EINVAL;
    +
    464  return false;
    +
    465  }
    +
    466 
    +
    467  qhashtbl_lock(tbl);
    +
    468 
    +
    469  uint32_t hash = qhashmurmur3_32(name, strlen(name));
    +
    470  int idx = hash % tbl->range;
    +
    471 
    +
    472  // find key
    +
    473  bool found = false;
    +
    474  qhashtbl_obj_t *prev = NULL;
    +
    475  qhashtbl_obj_t *obj;
    +
    476  for (obj = tbl->slots[idx]; obj != NULL; obj = obj->next) {
    +
    477  if (obj->hash == hash && !strcmp(obj->name, name)) {
    +
    478  // adjust link
    +
    479  if (prev == NULL)
    +
    480  tbl->slots[idx] = obj->next;
    +
    481  else
    +
    482  prev->next = obj->next;
    +
    483 
    +
    484  // remove
    +
    485  free(obj->name);
    +
    486  free(obj->data);
    +
    487  free(obj);
    +
    488 
    +
    489  found = true;
    +
    490  tbl->num--;
    +
    491  break;
    +
    492  }
    +
    493 
    +
    494  prev = obj;
    +
    495  }
    +
    496 
    +
    497  qhashtbl_unlock(tbl);
    +
    498 
    +
    499  if (found == false)
    +
    500  errno = ENOENT;
    +
    501 
    +
    502  return found;
    +
    503 }
    +
    504 
    +
    505 /**
    +
    506  * qhashtbl->getnext(): Get next element.
    +
    507  *
    +
    508  * @param tbl qhashtbl_t container pointer.
    +
    509  * @param obj found data will be stored in this object
    +
    510  * @param newmem whether or not to allocate memory for the data.
    +
    511  *
    +
    512  * @return true if found otherwise returns false
    +
    513  * @retval errno will be set in error condition.
    +
    514  * - ENOENT : No next element.
    +
    515  * - EINVAL : Invalid argument.
    +
    516  * - ENOMEM : Memory allocation failure.
    +
    517  *
    +
    518  * @code
    +
    519  * qhashtbl_t *tbl = qhashtbl(0, 0);
    +
    520  * (...add data into list...)
    +
    521  *
    +
    522  * qhashtbl_obj_t obj;
    +
    523  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    524  * tbl->lock(tbl); // lock it when thread condition is expected
    +
    525  * while(tbl->getnext(tbl, &obj, false) == true) { // newmem is false
    +
    526  * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    +
    527  * obj.name, (char*)obj.data, obj.size);
    +
    528  * // do free obj.name and obj.data if newmem was set to true;
    +
    529  * }
    +
    530  * tbl->unlock(tbl);
    +
    531  * @endcode
    +
    532  *
    +
    533  * @note
    +
    534  * locking must be provided on user code when all element scan must be
    +
    535  * guaranteed where multiple threads concurrently update the table.
    +
    536  * It's ok not to lock the table on the user code even in thread condition,
    +
    537  * when concurreny is importand and all element scan in a path doesn't need
    +
    538  * to be guaranteed. In this case, new data inserted during the traversal
    +
    539  * will be show up in this scan or next scan. Make sure newmem flag is set
    +
    540  * if deletion is expected during the scan.
    +
    541  * Object obj should be initialized with 0 by using memset() before first call.
    +
    542  */
    +
    543 bool qhashtbl_getnext(qhashtbl_t *tbl, qhashtbl_obj_t *obj, const bool newmem) {
    +
    544  if (obj == NULL) {
    +
    545  errno = EINVAL;
    +
    546  return NULL;
    +
    547  }
    +
    548 
    +
    549  qhashtbl_lock(tbl);
    +
    550 
    +
    551  bool found = false;
    +
    552 
    +
    553  qhashtbl_obj_t *cursor = NULL;
    +
    554  int idx = 0;
    +
    555  if (obj->name != NULL) {
    +
    556  idx = (obj->hash % tbl->range) + 1;
    +
    557  cursor = obj->next;
    +
    558  }
    +
    559 
    +
    560  if (cursor != NULL) {
    +
    561  // has link
    +
    562  found = true;
    +
    563  } else {
    +
    564  // search from next index
    +
    565  for (; idx < tbl->range; idx++) {
    +
    566  if (tbl->slots[idx] != NULL) {
    +
    567  cursor = tbl->slots[idx];
    +
    568  found = true;
    +
    569  break;
    +
    570  }
    +
    571  }
    +
    572  }
    +
    573 
    +
    574  if (cursor != NULL) {
    +
    575  if (newmem == true) {
    +
    576  obj->name = strdup(cursor->name);
    +
    577  obj->data = malloc(cursor->size);
    +
    578  if (obj->name == NULL || obj->data == NULL) {
    +
    579  DEBUG("getnext(): Unable to allocate memory.");
    +
    580  free(obj->name);
    +
    581  free(obj->data);
    +
    582  qhashtbl_unlock(tbl);
    +
    583  errno = ENOMEM;
    +
    584  return false;
    +
    585  }
    +
    586  memcpy(obj->data, cursor->data, cursor->size);
    +
    587  obj->size = cursor->size;
    +
    588  } else {
    +
    589  obj->name = cursor->name;
    +
    590  obj->data = cursor->data;
    +
    591  }
    +
    592  obj->hash = cursor->hash;
    +
    593  obj->size = cursor->size;
    +
    594  obj->next = cursor->next;
    +
    595 
    +
    596  }
    +
    597 
    +
    598  qhashtbl_unlock(tbl);
    +
    599 
    +
    600  if (found == false)
    +
    601  errno = ENOENT;
    +
    602 
    +
    603  return found;
    +
    604 }
    +
    605 
    +
    606 /**
    +
    607  * qhashtbl->size(): Returns the number of keys in this hashtable.
    +
    608  *
    +
    609  * @param tbl qhashtbl_t container pointer.
    +
    610  *
    +
    611  * @return number of elements stored
    +
    612  */
    +
    613 size_t qhashtbl_size(qhashtbl_t *tbl) {
    +
    614  return tbl->num;
    +
    615 }
    +
    616 
    +
    617 /**
    +
    618  * qhashtbl->clear(): Clears this hashtable so that it contains no keys.
    +
    619  *
    +
    620  * @param tbl qhashtbl_t container pointer.
    +
    621  */
    +
    622 void qhashtbl_clear(qhashtbl_t *tbl) {
    +
    623  qhashtbl_lock(tbl);
    +
    624  int idx;
    +
    625  for (idx = 0; idx < tbl->range && tbl->num > 0; idx++) {
    +
    626  if (tbl->slots[idx] == NULL)
    +
    627  continue;
    +
    628  qhashtbl_obj_t *obj = tbl->slots[idx];
    +
    629  tbl->slots[idx] = NULL;
    +
    630  while (obj != NULL) {
    +
    631  qhashtbl_obj_t *next = obj->next;
    +
    632  free(obj->name);
    +
    633  free(obj->data);
    +
    634  free(obj);
    +
    635  obj = next;
    +
    636 
    +
    637  tbl->num--;
    +
    638  }
    +
    639  }
    +
    640 
    +
    641  qhashtbl_unlock(tbl);
    +
    642 }
    +
    643 
    +
    644 /**
    +
    645  * qhashtbl->debug(): Print hash table for debugging purpose
    +
    646  *
    +
    647  * @param tbl qhashtbl_t container pointer.
    +
    648  * @param out output stream
    +
    649  *
    +
    650  * @return true if successful, otherwise returns false.
    +
    651  * @retval errno will be set in error condition.
    +
    652  * - EIO : Invalid output stream.
    +
    653  */
    +
    654 bool qhashtbl_debug(qhashtbl_t *tbl, FILE *out) {
    +
    655  if (out == NULL) {
    +
    656  errno = EIO;
    +
    657  return false;
    +
    658  }
    +
    659 
    +
    660  qhashtbl_obj_t obj;
    +
    661  memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call
    +
    662  qhashtbl_lock(tbl);
    +
    663  while (tbl->getnext(tbl, &obj, false) == true) {
    +
    664  fprintf(out, "%s=", obj.name);
    +
    665  _q_textout(out, obj.data, obj.size, MAX_HUMANOUT);
    +
    666  fprintf(out, " (%zu, %08x)\n", obj.size, obj.hash);
    +
    667  }
    +
    668  qhashtbl_unlock(tbl);
    +
    669 
    +
    670  return true;
    +
    671 }
    +
    672 
    +
    673 /**
    +
    674  * qhashtbl->lock(): Enter critical section.
    +
    675  *
    +
    676  * @param tbl qhashtbl_t container pointer.
    +
    677  *
    +
    678  * @note
    +
    679  * From user side, normally locking operation is only needed when traverse
    +
    680  * all elements using qhashtbl->getnext().
    +
    681  *
    +
    682  * @note
    +
    683  * This operation will do nothing if QHASHTBL_THREADSAFE option was not
    +
    684  * given at the initialization time.
    +
    685  */
    +
    686 void qhashtbl_lock(qhashtbl_t *tbl) {
    +
    687  Q_MUTEX_ENTER(tbl->qmutex);
    +
    688 }
    +
    689 
    +
    690 /**
    +
    691  * qhashtbl->unlock(): Leave critical section.
    +
    692  *
    +
    693  * @param tbl qhashtbl_t container pointer.
    +
    694  *
    +
    695  * @note
    +
    696  * This operation will do nothing if QHASHTBL_THREADSAFE option was not
    +
    697  * given at the initialization time.
    +
    698  */
    +
    699 void qhashtbl_unlock(qhashtbl_t *tbl) {
    +
    700  Q_MUTEX_LEAVE(tbl->qmutex);
    +
    701 }
    +
    702 
    +
    703 /**
    +
    704  * qhashtbl->free(): De-allocate hash table
    +
    705  *
    +
    706  * @param tbl qhashtbl_t container pointer.
    +
    707  */
    +
    708 void qhashtbl_free(qhashtbl_t *tbl) {
    +
    709  qhashtbl_lock(tbl);
    +
    710  qhashtbl_clear(tbl);
    +
    711  free(tbl->slots);
    +
    712  qhashtbl_unlock(tbl);
    +
    713  Q_MUTEX_DESTROY(tbl->qmutex);
    +
    714  free(tbl);
    +
    715 }
    +
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition: qhash.c:263
    +
    bool qhashtbl_remove(qhashtbl_t *tbl, const char *name)
    qhashtbl->remove(): Remove an object from this table.
    Definition: qhashtbl.c:461
    +
    bool qhashtbl_debug(qhashtbl_t *tbl, FILE *out)
    qhashtbl->debug(): Print hash table for debugging purpose
    Definition: qhashtbl.c:654
    +
    void qhashtbl_clear(qhashtbl_t *tbl)
    qhashtbl->clear(): Clears this hashtable so that it contains no keys.
    Definition: qhashtbl.c:622
    +
    int64_t qhashtbl_getint(qhashtbl_t *tbl, const char *name)
    qhashtbl->getint(): Finds an object with given name and returns as integer type.
    Definition: qhashtbl.c:439
    +
    void qhashtbl_unlock(qhashtbl_t *tbl)
    qhashtbl->unlock(): Leave critical section.
    Definition: qhashtbl.c:699
    +
    #define DEFAULT_INDEX_RANGE
    Definition: qhashtbl.c:101
    +
    void qhashtbl_free(qhashtbl_t *tbl)
    qhashtbl->free(): De-allocate hash table
    Definition: qhashtbl.c:708
    +
    void * qhashtbl_get(qhashtbl_t *tbl, const char *name, size_t *size, bool newmem)
    qhashtbl->get(): Get an object from this table.
    Definition: qhashtbl.c:363
    +
    bool qhashtbl_getnext(qhashtbl_t *tbl, qhashtbl_obj_t *obj, const bool newmem)
    qhashtbl->getnext(): Get next element.
    Definition: qhashtbl.c:543
    +
    size_t qhashtbl_size(qhashtbl_t *tbl)
    qhashtbl->size(): Returns the number of keys in this hashtable.
    Definition: qhashtbl.c:613
    +
    void qhashtbl_lock(qhashtbl_t *tbl)
    qhashtbl->lock(): Enter critical section.
    Definition: qhashtbl.c:686
    +
    bool qhashtbl_putstr(qhashtbl_t *tbl, const char *name, const char *str)
    qhashtbl->putstr(): Put a string into this table.
    Definition: qhashtbl.c:279
    +
    bool qhashtbl_put(qhashtbl_t *tbl, const char *name, const void *data, size_t size)
    qhashtbl->put(): Put an object into this table.
    Definition: qhashtbl.c:198
    +
    bool qhashtbl_putstrf(qhashtbl_t *tbl, const char *name, const char *format,...)
    qhashtbl->putstrf(): Put a formatted string into this table.
    Definition: qhashtbl.c:295
    +
    qhashtbl_t * qhashtbl(size_t range, int options)
    Initialize hash table.
    Definition: qhashtbl.c:127
    +
    char * qhashtbl_getstr(qhashtbl_t *tbl, const char *name, const bool newmem)
    qhashtbl->getstr(): Finds an object and returns as string type.
    Definition: qhashtbl.c:422
    +
    bool qhashtbl_putint(qhashtbl_t *tbl, const char *name, const int64_t num)
    qhashtbl->putint(): Put a integer into this table as string type.
    Definition: qhashtbl.c:323
    diff --git a/doc/html/qhttpclient_8c.html b/doc/html/qhttpclient_8c.html index ae5b6163..4911ebb9 100644 --- a/doc/html/qhttpclient_8c.html +++ b/doc/html/qhttpclient_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qhttpclient.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qhttpclient.c File Reference
    +
    +
    qhttpclient.c File Reference
    @@ -71,111 +71,133 @@

    Go to the source code of this file.

    - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +

    +

    Macros

    #define HTTP_NO_RESPONSE   (0)
    +#define HTTP_NO_RESPONSE   (0)
     
    #define HTTP_CODE_CONTINUE   (100)
    +#define HTTP_CODE_CONTINUE   (100)
     
    #define HTTP_CODE_OK   (200)
    +#define HTTP_CODE_OK   (200)
     
    #define HTTP_CODE_CREATED   (201)
    +#define HTTP_CODE_CREATED   (201)
     
    #define HTTP_CODE_NO_CONTENT   (204)
    +#define HTTP_CODE_NO_CONTENT   (204)
     
    #define HTTP_CODE_MULTI_STATUS   (207)
    +#define HTTP_CODE_MULTI_STATUS   (207)
     
    #define HTTP_CODE_MOVED_TEMPORARILY   (302)
    +#define HTTP_CODE_MOVED_TEMPORARILY   (302)
     
    #define HTTP_CODE_NOT_MODIFIED   (304)
    +#define HTTP_CODE_NOT_MODIFIED   (304)
     
    #define HTTP_CODE_BAD_REQUEST   (400)
    +#define HTTP_CODE_BAD_REQUEST   (400)
     
    #define HTTP_CODE_FORBIDDEN   (403)
    +#define HTTP_CODE_FORBIDDEN   (403)
     
    #define HTTP_CODE_NOT_FOUND   (404)
    +#define HTTP_CODE_NOT_FOUND   (404)
     
    #define HTTP_CODE_METHOD_NOT_ALLOWED   (405)
    +#define HTTP_CODE_METHOD_NOT_ALLOWED   (405)
     
    #define HTTP_CODE_REQUEST_TIME_OUT   (408)
    +#define HTTP_CODE_REQUEST_TIME_OUT   (408)
     
    #define HTTP_CODE_REQUEST_URI_TOO_LONG   (414)
    +#define HTTP_CODE_REQUEST_URI_TOO_LONG   (414)
     
    #define HTTP_CODE_INTERNAL_SERVER_ERROR   (500)
    +#define HTTP_CODE_INTERNAL_SERVER_ERROR   (500)
     
    #define HTTP_CODE_NOT_IMPLEMENTED   (501)
    +#define HTTP_CODE_NOT_IMPLEMENTED   (501)
     
    #define HTTP_CODE_SERVICE_UNAVAILABLE   (503)
    +#define HTTP_CODE_SERVICE_UNAVAILABLE   (503)
     
    #define HTTP_PROTOCOL_11   "HTTP/1.1"
    +#define HTTP_PROTOCOL_11   "HTTP/1.1"
     
    #define SET_TCP_LINGER_TIMEOUT   (15) /*< linger seconds, 0 for disable */
    +#define SET_TCP_LINGER_TIMEOUT   (15) /*< linger seconds, 0 for disable */
     
    #define SET_TCP_NODELAY   (1) /*< 0 for disable */
    +#define SET_TCP_NODELAY   (1) /*< 0 for disable */
     
    #define MAX_SHUTDOWN_WAIT   (100) /*< maximum shutdown wait, unit is ms */
    +#define MAX_SHUTDOWN_WAIT   (100) /*< maximum shutdown wait, unit is ms */
     
    #define MAX_ATOMIC_DATA_SIZE   (32 * 1024) /*< maximum sending bytes */
    +#define MAX_ATOMIC_DATA_SIZE   (32 * 1024) /*< maximum sending bytes */
     
    - - - - + + + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - + - + - + - + - + - +

    +

    Functions

    qhttpclient_t * qhttpclient (const char *destname, int port)
     Initialize & create new HTTP client.
     
    qhttpclient_t * qhttpclient (const char *destname, int port)
     Initialize & create new HTTP client. More...
     
    static bool setssl (qhttpclient_t *client)
     qhttpclient->setssl(): Sets connection to HTTPS connection
     qhttpclient->setssl(): Sets connection to HTTPS connection More...
     
    static void settimeout (qhttpclient_t *client, int timeoutms)
     qhttpclient->settimeout(): Sets connection wait timeout.
     qhttpclient->settimeout(): Sets connection wait timeout. More...
     
    static void setkeepalive (qhttpclient_t *client, bool keepalive)
     qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
     qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off. More...
     
    static void setuseragent (qhttpclient_t *client, const char *useragent)
     qhttpclient->setuseragent(): Sets user-agent string.
     qhttpclient->setuseragent(): Sets user-agent string. More...
     
    static bool open_ (qhttpclient_t *client)
     qhttpclient->open(): Opens a connection to the remote host.
     qhttpclient->open(): Opens a connection to the remote host. More...
     
    static bool head (qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
     qhttpclient->head(): Sends a HEAD request.
     qhttpclient->head(): Sends a HEAD request. More...
     
    static bool get (qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
     qhttpclient->get(): Downloads a file from the remote host using GET method.
     qhttpclient->get(): Downloads a file from the remote host using GET method. More...
     
    static bool put (qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
     qhttpclient->put(): Uploads a file to the remote host using PUT method.
     qhttpclient->put(): Uploads a file to the remote host using PUT method. More...
     
    static void * cmd (qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
     qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.
     
    static void * cmd (qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
     qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response. More...
     
    static bool sendrequest (qhttpclient_t *client, const char *method, const char *uri, qlisttbl_t *reqheaders)
     qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
     qhttpclient->sendrequest(): Sends a HTTP request to the remote host. More...
     
    static int readresponse (qhttpclient_t *client, qlisttbl_t *resheaders, off_t *contentlength)
     qhttpclient->readresponse(): Reads HTTP response header from the remote host.
     qhttpclient->readresponse(): Reads HTTP response header from the remote host. More...
     
    static ssize_t gets_ (qhttpclient_t *client, char *buf, size_t bufsize)
     qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
     qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream. More...
     
    static ssize_t read_ (qhttpclient_t *client, void *buf, size_t nbytes)
     qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
     qhttpclient->read(): Reads data from a HTTP/HTTPS stream. More...
     
    static ssize_t write_ (qhttpclient_t *client, const void *buf, size_t nbytes)
     qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
     qhttpclient->write(): Writes data to a HTTP/HTTPS stream. More...
     
    static off_t recvfile (qhttpclient_t *client, int fd, off_t nbytes)
     qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save into a file descriptor.
     qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save into a file descriptor. More...
     
    static off_t sendfile_ (qhttpclient_t *client, int fd, off_t nbytes)
     qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
     qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream. More...
     
    static bool _close (qhttpclient_t *client)
     qhttpclient->close(): Closes the connection.
     qhttpclient->close(): Closes the connection. More...
     
    static void _free (qhttpclient_t *client)
     qhttpclient->free(): Free object.
     qhttpclient->free(): Free object. More...
     

    Detailed Description

    @@ -187,7 +209,7 @@
    int main(void) {
    // create new HTTP client
    -
    qhttpclient_t *httpclient = qhttpclient("https://secure.qdecoder.org", 0);
    +
    qhttpclient_t *httpclient = qhttpclient("https://secure.qdecoder.org", 0);
    if(httpclient == NULL) return -1;
    // open file for writing
    @@ -198,7 +220,7 @@
    }
    // container for storing response headers for debugging purpose
    -
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    // download
    off_t nSavesize = 0;
    @@ -211,7 +233,7 @@
    // print out debugging info
    printf("%s %d, %d bytes saved\n", (bRet?"Success":"Failed"), nRescode,
    -
    (int)nSavesize);
    +
    (int)nSavesize);
    resheaders->debug(resheaders, stdout);
    // de-allocate HTTP client object
    @@ -232,11 +254,11 @@
    Expires=Fri, 18 Feb 2011 23:40:50 GMT? (30)
    Connection=close? (6)
    Content-Type=text/plain? (11)
    -
    qhttpclient_t * qhttpclient(const char *destname, int port)
    Initialize & create new HTTP client.
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    +
    qhttpclient_t * qhttpclient(const char *destname, int port)
    Initialize & create new HTTP client.
    Definition: qhttpclient.c:245
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150

    Example code for multiple PUT operation using same keep-alive connection.

    // create new HTTP client
    -
    qhttpclient_t *httpclient = qhttpclient("www.qdecoder.org", 80);
    +
    qhttpclient_t *httpclient = qhttpclient("www.qdecoder.org", 80);
    if(httpclient == NULL) return;
    // set options
    @@ -256,368 +278,15 @@
    httpclient->free(httpclient);

    Definition in file qhttpclient.c.

    -

    Macro Definition Documentation

    - -

    ◆ HTTP_NO_RESPONSE

    - -
    -
    - - - - -
    #define HTTP_NO_RESPONSE   (0)
    -
    - -

    Definition at line 188 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_CONTINUE

    - -
    -
    - - - - -
    #define HTTP_CODE_CONTINUE   (100)
    -
    - -

    Definition at line 189 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_OK

    - -
    -
    - - - - -
    #define HTTP_CODE_OK   (200)
    -
    - -

    Definition at line 190 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_CREATED

    - -
    -
    - - - - -
    #define HTTP_CODE_CREATED   (201)
    -
    - -

    Definition at line 191 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_NO_CONTENT

    - -
    -
    - - - - -
    #define HTTP_CODE_NO_CONTENT   (204)
    -
    - -

    Definition at line 192 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_MULTI_STATUS

    - -
    -
    - - - - -
    #define HTTP_CODE_MULTI_STATUS   (207)
    -
    - -

    Definition at line 193 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_MOVED_TEMPORARILY

    - -
    -
    - - - - -
    #define HTTP_CODE_MOVED_TEMPORARILY   (302)
    -
    - -

    Definition at line 194 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_NOT_MODIFIED

    - -
    -
    - - - - -
    #define HTTP_CODE_NOT_MODIFIED   (304)
    -
    - -

    Definition at line 195 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_BAD_REQUEST

    - -
    -
    - - - - -
    #define HTTP_CODE_BAD_REQUEST   (400)
    -
    - -

    Definition at line 196 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_FORBIDDEN

    - -
    -
    - - - - -
    #define HTTP_CODE_FORBIDDEN   (403)
    -
    - -

    Definition at line 197 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_NOT_FOUND

    - -
    -
    - - - - -
    #define HTTP_CODE_NOT_FOUND   (404)
    -
    - -

    Definition at line 198 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_METHOD_NOT_ALLOWED

    - -
    -
    - - - - -
    #define HTTP_CODE_METHOD_NOT_ALLOWED   (405)
    -
    - -

    Definition at line 199 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_REQUEST_TIME_OUT

    - -
    -
    - - - - -
    #define HTTP_CODE_REQUEST_TIME_OUT   (408)
    -
    - -

    Definition at line 200 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_REQUEST_URI_TOO_LONG

    - -
    -
    - - - - -
    #define HTTP_CODE_REQUEST_URI_TOO_LONG   (414)
    -
    - -

    Definition at line 201 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_INTERNAL_SERVER_ERROR

    - -
    -
    - - - - -
    #define HTTP_CODE_INTERNAL_SERVER_ERROR   (500)
    -
    - -

    Definition at line 202 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_NOT_IMPLEMENTED

    - -
    -
    - - - - -
    #define HTTP_CODE_NOT_IMPLEMENTED   (501)
    -
    - -

    Definition at line 203 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_CODE_SERVICE_UNAVAILABLE

    - -
    -
    - - - - -
    #define HTTP_CODE_SERVICE_UNAVAILABLE   (503)
    -
    - -

    Definition at line 204 of file qhttpclient.c.

    - -
    -
    - -

    ◆ HTTP_PROTOCOL_11

    - -
    -
    - - - - -
    #define HTTP_PROTOCOL_11   "HTTP/1.1"
    -
    - -

    Definition at line 206 of file qhttpclient.c.

    - -
    -
    - -

    ◆ SET_TCP_LINGER_TIMEOUT

    - -
    -
    - - - - -
    #define SET_TCP_LINGER_TIMEOUT   (15) /*< linger seconds, 0 for disable */
    -
    - -

    Definition at line 211 of file qhttpclient.c.

    - -
    -
    - -

    ◆ SET_TCP_NODELAY

    - -
    -
    - - - - -
    #define SET_TCP_NODELAY   (1) /*< 0 for disable */
    -
    - -

    Definition at line 212 of file qhttpclient.c.

    - -
    -
    - -

    ◆ MAX_SHUTDOWN_WAIT

    - -
    -
    - - - - -
    #define MAX_SHUTDOWN_WAIT   (100) /*< maximum shutdown wait, unit is ms */
    -
    - -

    Definition at line 213 of file qhttpclient.c.

    - -
    -
    - -

    ◆ MAX_ATOMIC_DATA_SIZE

    - -
    -
    - - - - -
    #define MAX_ATOMIC_DATA_SIZE   (32 * 1024) /*< maximum sending bytes */
    -
    - -

    Definition at line 214 of file qhttpclient.c.

    - -
    -
    -

    Function Documentation

    - -

    ◆ qhttpclient()

    +

    Function Documentation

    + +

    ◆ qhttpclient()

    - + @@ -645,20 +314,20 @@

    Returns
    HTTP client object if succcessful, otherwise returns NULL.
    -
    -
    qhttpclient_t *client = qhttpclient("www.qdecoder.org", 80);
    -
    qhttpclient_t *client = qhttpclient("http://www.qdecoder.org", 0);
    -
    qhttpclient_t *client = qhttpclient("http://www.qdecoder.org:80", 0);
    -
    qhttpclient_t *client = qhttpclient("https://www.qdecoder.org", 0);
    -
    qhttpclient_t *client = qhttpclient("https://www.qdecoder.org:443", 0);
    +
    qhttpclient_t *client = qhttpclient("1.2.3.4", 80);
    +
    qhttpclient_t *client = qhttpclient("www.qdecoder.org", 80);
    +
    qhttpclient_t *client = qhttpclient("http://www.qdecoder.org", 0);
    +
    qhttpclient_t *client = qhttpclient("http://www.qdecoder.org:80", 0);
    +
    qhttpclient_t *client = qhttpclient("https://www.qdecoder.org", 0);
    +
    qhttpclient_t *client = qhttpclient("https://www.qdecoder.org:443", 0);
    Note
    Keep-alive feature is turned off by default. Turn it on by calling setkeepalive(). If destname is URI string starting with "https://", setssl() will be called internally.

    Definition at line 245 of file qhttpclient.c.

    - -

    ◆ setssl()

    + +

    ◆ setssl()

    @@ -694,8 +363,8 @@

    -

    ◆ settimeout()

    + +

    ◆ settimeout()

    @@ -743,8 +412,8 @@

    -

    ◆ setkeepalive()

    + +

    ◆ setkeepalive()

    @@ -792,8 +461,8 @@

    -

    ◆ setuseragent()

    + +

    ◆ setuseragent()

    @@ -840,8 +509,8 @@

    -

    ◆ open_()

    + +

    ◆ open_()

    @@ -879,8 +548,8 @@

    -

    ◆ head()

    + +

    ◆ head()

    @@ -945,12 +614,12 @@

    Returns
    true if successful(got 200 response), otherwise returns false
    main() {
    // create new HTTP client
    -
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    if(httpclient == NULL) return;
    // set additional custom headers
    -
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    // send HEAD request
    int nRescode = 0;
    @@ -978,8 +647,8 @@

    -

    ◆ get()

    + +

    ◆ get()

    @@ -1083,15 +752,15 @@

    main() {
    // create new HTTP client
    -
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    if(httpclient == NULL) return;
    // open file
    int nFd = open("/tmp/test.data", O_WRONLY | O_CREAT, 0644);
    // set additional custom headers
    -
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    // set userdata
    struct userdata mydata;
    @@ -1130,8 +799,8 @@

    -

    ◆ put()

    + +

    ◆ put()

    @@ -1235,7 +904,7 @@

    main() {
    // create new HTTP client
    -
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    if(httpclient == NULL) return;
    // open file
    @@ -1245,7 +914,7 @@

    time_t nFileDate = ...;

    // set additional custom headers
    -
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    reqheaders->putstr(reqheaders, "X-FILE-MD5SUM", pFileMd5sum);
    reqheaders->putInt(reqheaders, "X-FILE-DATE", nFileDate);
    @@ -1255,7 +924,7 @@

    // send file
    int nRescode = 0;
    -
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    bool bRet = httpclient->put(httpclient,
    "/img/qdecoder.png", nFd, nFileSize,
    &nRescode,
    @@ -1284,8 +953,8 @@

    -

    ◆ cmd()

    + +

    ◆ cmd()

    @@ -1294,7 +963,7 @@

    qhttpclient_t * qhttpclient qhttpclient_t* qhttpclient ( const char *  destname,
    - + @@ -1360,7 +1029,7 @@

    -

    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.

    +

    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.

    Parameters

    static void * cmd static void* cmd ( qhttpclient_t *  client,
    @@ -1396,8 +1065,8 @@

    -

    ◆ sendrequest()

    + +

    ◆ sendrequest()

    @@ -1454,7 +1123,7 @@

    Returns
    true if successful, otherwise returns false
    Note
    Default headers(Host, User-Agent, Connection) will be used if reqheaders does not have those headers in it.
    -
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    reqheaders->putstr(reqheaders, "Date", qTimeGetGmtStaticStr(0), true);
    httpclient->sendrequest(client,
    @@ -1464,8 +1133,8 @@

    -

    ◆ readresponse()

    + +

    ◆ readresponse()

    // read response
    -
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    off_t clength;
    int rescode = httpclient->readresponse(client, resheaders, &clength);
    if(clength > 0) {
    @@ -1531,8 +1200,8 @@

    -

    ◆ gets_()

    + +

    ◆ gets_()

    @@ -1587,8 +1256,8 @@

    -

    ◆ read_()

    + +

    ◆ read_()

    @@ -1648,8 +1317,8 @@

    -

    ◆ write_()

    + +

    ◆ write_()

    @@ -1703,8 +1372,8 @@

    -

    ◆ recvfile()

    + +

    ◆ recvfile()

    @@ -1758,8 +1427,8 @@

    -

    ◆ sendfile_()

    + +

    ◆ sendfile_()

    @@ -1813,8 +1482,8 @@

    -

    ◆ _close()

    + +

    ◆ _close()

    @@ -1851,8 +1520,8 @@

    -

    ◆ _free()

    + +

    ◆ _free()

    @@ -1895,7 +1564,7 @@

    diff --git a/doc/html/qhttpclient_8c.js b/doc/html/qhttpclient_8c.js index 5a5ac5a0..cae565a1 100644 --- a/doc/html/qhttpclient_8c.js +++ b/doc/html/qhttpclient_8c.js @@ -1,6 +1,28 @@ var qhttpclient_8c = [ - [ "qhttpclient", "qhttpclient_8c.html#aa5f4b63d794316d3cdeafee2023b48c9", null ], + [ "HTTP_NO_RESPONSE", "qhttpclient_8c.html#ad02ab7af04409020264521a798de2a5a", null ], + [ "HTTP_CODE_CONTINUE", "qhttpclient_8c.html#ad1f324698e12fefadfc2ec5d16ce6a5b", null ], + [ "HTTP_CODE_OK", "qhttpclient_8c.html#a471de168f41ecf5e8d62a43fea28b5d7", null ], + [ "HTTP_CODE_CREATED", "qhttpclient_8c.html#a00c11c6c6bf42cf1fb483c273454cf45", null ], + [ "HTTP_CODE_NO_CONTENT", "qhttpclient_8c.html#a7e41c8a47383e2bdf4b34ce5510bc9b6", null ], + [ "HTTP_CODE_MULTI_STATUS", "qhttpclient_8c.html#a463a0b7aed650158725b205bc1bd0de8", null ], + [ "HTTP_CODE_MOVED_TEMPORARILY", "qhttpclient_8c.html#a33f32423ccbf3a2a61f56cc5aee18cd8", null ], + [ "HTTP_CODE_NOT_MODIFIED", "qhttpclient_8c.html#a225a5999b63c699c4492a8412b315452", null ], + [ "HTTP_CODE_BAD_REQUEST", "qhttpclient_8c.html#a16c7e21f17349c2515fe0134f9b7ffd4", null ], + [ "HTTP_CODE_FORBIDDEN", "qhttpclient_8c.html#ac68e0a1d868dad52967d757dab5220db", null ], + [ "HTTP_CODE_NOT_FOUND", "qhttpclient_8c.html#a20eb5cc8af538c827de0f7ed9811243c", null ], + [ "HTTP_CODE_METHOD_NOT_ALLOWED", "qhttpclient_8c.html#a7c8d72a7d4eda32ad9a6211958463e97", null ], + [ "HTTP_CODE_REQUEST_TIME_OUT", "qhttpclient_8c.html#a78c49465c70c576daa60e34a84a9e47c", null ], + [ "HTTP_CODE_REQUEST_URI_TOO_LONG", "qhttpclient_8c.html#a0023f7e49348c8c52b2fb8b547202ed3", null ], + [ "HTTP_CODE_INTERNAL_SERVER_ERROR", "qhttpclient_8c.html#a6d47746cc8b934944ed4f05308b2da63", null ], + [ "HTTP_CODE_NOT_IMPLEMENTED", "qhttpclient_8c.html#a9e272d1190eb37ce4447a9021f68a0c4", null ], + [ "HTTP_CODE_SERVICE_UNAVAILABLE", "qhttpclient_8c.html#a00a05677bf92a38abf5be28b5ce0d294", null ], + [ "HTTP_PROTOCOL_11", "qhttpclient_8c.html#aba396af03e2875b5ddedee43ad3c4707", null ], + [ "SET_TCP_LINGER_TIMEOUT", "qhttpclient_8c.html#a337b3f9b61cacbcde92a255249e4aae5", null ], + [ "SET_TCP_NODELAY", "qhttpclient_8c.html#a8c266040b26660f0c96255cadd8ee087", null ], + [ "MAX_SHUTDOWN_WAIT", "qhttpclient_8c.html#a34f24b8ca491cffac52bdd24140cd923", null ], + [ "MAX_ATOMIC_DATA_SIZE", "qhttpclient_8c.html#a93983c38a271da5bde55becc02135bdb", null ], + [ "qhttpclient", "qhttpclient_8c.html#acba5e1d5c9b1666945f7b0ca4da0785b", null ], [ "setssl", "qhttpclient_8c.html#a5c8eb3bb63df37fe248d858715894870", null ], [ "settimeout", "qhttpclient_8c.html#a12e0c5c0a30dafae0ece655d6095a75c", null ], [ "setkeepalive", "qhttpclient_8c.html#a7ee9c5a86a3f561a5dcfe201ff20c350", null ], @@ -9,7 +31,7 @@ var qhttpclient_8c = [ "head", "qhttpclient_8c.html#ac3c34055be115314ffe7f18c3d19eb54", null ], [ "get", "qhttpclient_8c.html#af16a3eb0912e8b5d4896466632d9c843", null ], [ "put", "qhttpclient_8c.html#ae9ac3bd43c11509c0cbd8fb284bf13eb", null ], - [ "cmd", "qhttpclient_8c.html#a1f90b08bc44bdf06a60dae398d7ee297", null ], + [ "cmd", "qhttpclient_8c.html#af152fd3b6a32a801dc70a03119b13154", null ], [ "sendrequest", "qhttpclient_8c.html#a1d31e08f7bc114b63cd3140f5f8dc259", null ], [ "readresponse", "qhttpclient_8c.html#a865df9ac070a17d0751e1083af770bb1", null ], [ "gets_", "qhttpclient_8c.html#ade555b391a0ff83b074eb16a5ba2192f", null ], diff --git a/doc/html/qhttpclient_8c_source.html b/doc/html/qhttpclient_8c_source.html index abf0fb82..c70713e5 100644 --- a/doc/html/qhttpclient_8c_source.html +++ b/doc/html/qhttpclient_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qhttpclient.c Source File @@ -20,8 +20,8 @@

    clientqhttpclient object pointer.
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1805 +52,1806 @@
    -
    qhttpclient.c
    +
    +
    qhttpclient.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qhttpclient.c HTTP client object.
    -
    31 *
    -
    32 * qhttpclient implements HTTP client.
    -
    33 *
    -
    34 * Example code for simple HTTP GET operation.
    -
    35 *
    -
    36 * @code
    -
    37 * #define REMOTE_URL "/robots.txt"
    -
    38 * #define SAVEFILE "/tmp/robots.txt"
    -
    39 *
    -
    40 * int main(void) {
    -
    41 * // create new HTTP client
    -
    42 * qhttpclient_t *httpclient = qhttpclient("https://secure.qdecoder.org", 0);
    -
    43 * if(httpclient == NULL) return -1;
    -
    44 *
    -
    45 * // open file for writing
    -
    46 * int nFd = open(SAVEFILE, O_CREAT | O_TRUNC | O_WRONLY, 0644);
    -
    47 * if(nFd < 0) {
    -
    48 * httpclient->free(httpclient);
    -
    49 * return -1;
    -
    50 * }
    -
    51 *
    -
    52 * // container for storing response headers for debugging purpose
    -
    53 * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    54 *
    -
    55 * // download
    -
    56 * off_t nSavesize = 0;
    -
    57 * int nRescode = 0;
    -
    58 * bool bRet = httpclient->get(httpclient, REMOTE_URL, nFd, &nSavesize,
    -
    59 * &nRescode, NULL, resheaders, NULL, NULL);
    -
    60 *
    -
    61 * // close file
    -
    62 * close(nFd);
    -
    63 *
    -
    64 * // print out debugging info
    -
    65 * printf("%s %d, %d bytes saved\n", (bRet?"Success":"Failed"), nRescode,
    -
    66 * (int)nSavesize);
    -
    67 * resheaders->debug(resheaders, stdout);
    -
    68 *
    -
    69 * // de-allocate HTTP client object
    -
    70 * httpclient->free(httpclient);
    -
    71 *
    -
    72 * return (bRet ? 0 : -1);
    -
    73 * }
    -
    74 *
    -
    75 * [Output]
    -
    76 * Success 200, 30 bytes saved
    -
    77 * Date=Fri, 11 Feb 2011 23:40:50 GMT? (30)
    -
    78 * Server=Apache? (7)
    -
    79 * Last-Modified=Sun, 15 Mar 2009 11:43:07 GMT? (30)
    -
    80 * ETag="2e5c9d-1e-46526d665c8c0"? (26)
    -
    81 * Accept-Ranges=bytes? (6)
    -
    82 * Content-Length=30? (3)
    -
    83 * Cache-Control=max-age=604800? (15)
    -
    84 * Expires=Fri, 18 Feb 2011 23:40:50 GMT? (30)
    -
    85 * Connection=close? (6)
    -
    86 * Content-Type=text/plain? (11)
    -
    87 * @endcode
    -
    88 *
    -
    89 * Example code for multiple PUT operation using same keep-alive connection.
    -
    90 *
    -
    91 * @code
    -
    92 * // create new HTTP client
    -
    93 * qhttpclient_t *httpclient = qhttpclient("www.qdecoder.org", 80);
    -
    94 * if(httpclient == NULL) return;
    -
    95 *
    -
    96 * // set options
    -
    97 * httpclient->setkeepalive(httpclient, true);
    -
    98 *
    -
    99 * // make a connection
    -
    100 * if(httpclient->open(httpclient) == false) return;
    -
    101 *
    -
    102 * // upload files
    -
    103 * httpclient->put(httpclient, ...);
    -
    104 * httpclient->put(httpclient, ...); // will be done within same connection.
    -
    105 *
    -
    106 * // close connection - not necessary if we call free() just after this.
    -
    107 * httpclient->close(httpclient);
    -
    108 *
    -
    109 * // de-allocate HTTP client object
    -
    110 * httpclient->free(httpclient);
    -
    111 * @endcode
    -
    112 */
    -
    113
    -
    114#ifndef DISABLE_QHTTPCLIENT
    -
    115
    -
    116#include <stdio.h>
    -
    117#include <stdlib.h>
    -
    118#include <stdbool.h>
    -
    119#include <string.h>
    -
    120#include <unistd.h>
    -
    121#include <fcntl.h>
    -
    122#include <errno.h>
    -
    123#include <sys/types.h>
    -
    124#include <sys/socket.h>
    -
    125#include <netinet/in.h>
    -
    126#include <netinet/tcp.h>
    -
    127#include <arpa/inet.h>
    -
    128
    -
    129#ifdef ENABLE_OPENSSL
    -
    130#include "openssl/ssl.h"
    -
    131#include "openssl/err.h"
    -
    132#endif
    -
    133
    -
    134#include "qinternal.h"
    -
    135#include "utilities/qio.h"
    -
    136#include "utilities/qstring.h"
    -
    137#include "utilities/qsocket.h"
    -
    138#include "containers/qlisttbl.h"
    -
    139#include "containers/qgrow.h"
    -
    140#include "extensions/qhttpclient.h"
    -
    141
    -
    142#ifndef _DOXYGEN_SKIP
    -
    143
    -
    144static bool open_(qhttpclient_t *client);
    -
    145static bool setssl(qhttpclient_t *client);
    -
    146static void settimeout(qhttpclient_t *client, int timeoutms);
    -
    147static void setkeepalive(qhttpclient_t *client, bool keepalive);
    -
    148static void setuseragent(qhttpclient_t *client, const char *agentname);
    -
    149
    -
    150static bool head(qhttpclient_t *client, const char *uri, int *rescode,
    -
    151 qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
    -
    152static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize,
    -
    153 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    -
    154 bool (*callback)(void *userdata, off_t recvbytes),
    -
    155 void *userdata);
    -
    156static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length,
    -
    157 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    -
    158 bool (*callback)(void *userdata, off_t sentbytes),
    -
    159 void *userdata);
    -
    160static void *cmd(qhttpclient_t *client, const char *method, const char *uri,
    -
    161 void *data, size_t size, int *rescode, size_t *contentslength,
    -
    162 qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
    -
    163
    -
    164static bool sendrequest(qhttpclient_t *client, const char *method,
    -
    165 const char *uri, qlisttbl_t *reqheaders);
    -
    166static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
    -
    167 off_t *contentlength);
    -
    168
    -
    169static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize);
    -
    170static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes);
    -
    171static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes);
    -
    172static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes);
    -
    173static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes);
    -
    174
    -
    175static bool _close(qhttpclient_t *client);
    -
    176static void _free(qhttpclient_t *client);
    -
    177
    -
    178// internal usages
    -
    179static bool _set_socket_option(int socket);
    -
    180static bool _parse_uri(const char *uri, bool *protocol, char *hostname,
    -
    181 size_t namesize, int *port);
    -
    182
    -
    183#endif
    -
    184
    -
    185//
    -
    186// HTTP RESPONSE CODE
    -
    187//
    -
    188#define HTTP_NO_RESPONSE (0)
    -
    189#define HTTP_CODE_CONTINUE (100)
    -
    190#define HTTP_CODE_OK (200)
    -
    191#define HTTP_CODE_CREATED (201)
    -
    192#define HTTP_CODE_NO_CONTENT (204)
    -
    193#define HTTP_CODE_MULTI_STATUS (207)
    -
    194#define HTTP_CODE_MOVED_TEMPORARILY (302)
    -
    195#define HTTP_CODE_NOT_MODIFIED (304)
    -
    196#define HTTP_CODE_BAD_REQUEST (400)
    -
    197#define HTTP_CODE_FORBIDDEN (403)
    -
    198#define HTTP_CODE_NOT_FOUND (404)
    -
    199#define HTTP_CODE_METHOD_NOT_ALLOWED (405)
    -
    200#define HTTP_CODE_REQUEST_TIME_OUT (408)
    -
    201#define HTTP_CODE_REQUEST_URI_TOO_LONG (414)
    -
    202#define HTTP_CODE_INTERNAL_SERVER_ERROR (500)
    -
    203#define HTTP_CODE_NOT_IMPLEMENTED (501)
    -
    204#define HTTP_CODE_SERVICE_UNAVAILABLE (503)
    -
    205
    -
    206#define HTTP_PROTOCOL_11 "HTTP/1.1"
    -
    207
    -
    208//
    -
    209// TCP SOCKET DEFINITION
    -
    210//
    -
    211#define SET_TCP_LINGER_TIMEOUT (15) /*< linger seconds, 0 for disable */
    -
    212#define SET_TCP_NODELAY (1) /*< 0 for disable */
    -
    213#define MAX_SHUTDOWN_WAIT (100) /*< maximum shutdown wait, unit is ms */
    -
    214#define MAX_ATOMIC_DATA_SIZE (32 * 1024) /*< maximum sending bytes */
    -
    215
    -
    216#ifdef ENABLE_OPENSSL
    -
    217struct SslConn {
    -
    218 SSL *ssl;
    -
    219 SSL_CTX *ctx;
    -
    220};
    -
    221#endif
    -
    222
    -
    223/**
    -
    224 * Initialize & create new HTTP client.
    -
    225 *
    -
    226 * @param destname remote address, one of IP address, FQDN domain name and URI.
    -
    227 * @param port remote port number. (can be 0 when destname is URI)
    -
    228 *
    -
    229 * @return HTTP client object if succcessful, otherwise returns NULL.
    -
    230 *
    -
    231 * @code
    -
    232 * qhttpclient_t *client = qhttpclient("1.2.3.4", 80);
    -
    233 * qhttpclient_t *client = qhttpclient("www.qdecoder.org", 80);
    -
    234 * qhttpclient_t *client = qhttpclient("http://www.qdecoder.org", 0);
    -
    235 * qhttpclient_t *client = qhttpclient("http://www.qdecoder.org:80", 0);
    -
    236 * qhttpclient_t *client = qhttpclient("https://www.qdecoder.org", 0);
    -
    237 * qhttpclient_t *client = qhttpclient("https://www.qdecoder.org:443", 0);
    -
    238 * @endcode
    -
    239 *
    -
    240 * @note
    -
    241 * Keep-alive feature is turned off by default. Turn it on by calling
    -
    242 * setkeepalive(). If destname is URI string starting with
    -
    243 * "https://", setssl() will be called internally.
    -
    244 */
    -
    245qhttpclient_t *qhttpclient(const char *destname, int port) {
    -
    246 bool ishttps = false;
    -
    247 char hostname[256];
    -
    248 if (port == 0 || strstr(destname, "://") != NULL) {
    -
    249 if (_parse_uri(destname, &ishttps, hostname, sizeof(hostname), &port)
    -
    250 == false) {
    -
    251 DEBUG("Can't parse URI %s", destname);
    -
    252 return NULL;
    -
    253 }
    -
    254
    -
    255 DEBUG("https: %d, hostname: %s, port:%d\n", ishttps, hostname, port);
    -
    256 } else {
    -
    257 qstrcpy(hostname, sizeof(hostname), destname);
    -
    258 }
    -
    259
    -
    260 // get remote address
    -
    261 struct sockaddr_in addr;
    -
    262 if (qsocket_get_addr(&addr, hostname, port) == false) {
    -
    263 return NULL;
    -
    264 }
    -
    265
    -
    266 // allocate object
    -
    267 qhttpclient_t *client = (qhttpclient_t *) malloc(sizeof(qhttpclient_t));
    -
    268 if (client == NULL)
    -
    269 return NULL;
    -
    270 memset((void *) client, 0, sizeof(qhttpclient_t));
    -
    271
    -
    272 // initialize object
    -
    273 client->socket = -1;
    -
    274
    -
    275 memcpy((void *) &client->addr, (void *) &addr, sizeof(client->addr));
    -
    276 client->hostname = strdup(hostname);
    -
    277 client->port = port;
    -
    278
    -
    279 // member methods
    -
    280 client->setssl = setssl;
    -
    281 client->settimeout = settimeout;
    -
    282 client->setkeepalive = setkeepalive;
    -
    283 client->setuseragent = setuseragent;
    -
    284
    -
    285 client->open = open_;
    -
    286
    -
    287 client->head = head;
    -
    288 client->get = get;
    -
    289 client->put = put;
    -
    290 client->cmd = cmd;
    -
    291
    -
    292 client->sendrequest = sendrequest;
    -
    293 client->readresponse = readresponse;
    -
    294
    -
    295 client->gets = gets_;
    -
    296 client->read = read_;
    -
    297 client->write = write_;
    -
    298 client->recvfile = recvfile;
    -
    299 client->sendfile = sendfile_;
    -
    300
    -
    301 client->close = _close;
    -
    302 client->free = _free;
    -
    303
    -
    304 // init client
    -
    305 settimeout(client, 0);
    -
    306 setkeepalive(client, false);
    -
    307 setuseragent(client, QHTTPCLIENT_NAME);
    -
    308 if (ishttps == true)
    -
    309 setssl(client);
    -
    310
    -
    311 return client;
    -
    312}
    -
    313
    -
    314/**
    -
    315 * qhttpclient->setssl(): Sets connection to HTTPS connection
    -
    316 *
    -
    317 * @param client qhttpclient object pointer
    -
    318 *
    -
    319 * @code
    -
    320 * httpclient->setssl(httpclient);
    -
    321 * @endcode
    -
    322 */
    -
    323static bool setssl(qhttpclient_t *client) {
    -
    324#ifdef ENABLE_OPENSSL
    -
    325 static bool initialized = false;
    -
    326
    -
    327 if (client->socket >= 0) {
    -
    328 // must be set before making a connection.
    -
    329 return false;
    -
    330 }
    -
    331
    -
    332 // init openssl
    -
    333 if (initialized == false) {
    -
    334 initialized = true;
    -
    335 SSL_load_error_strings();
    -
    336 SSL_library_init();
    -
    337 }
    -
    338
    -
    339 // allocate ssl structure
    -
    340 if (client->ssl == NULL) {
    -
    341 client->ssl = malloc(sizeof(struct SslConn));
    -
    342 if (client->ssl == NULL) return false;
    -
    343 memset(client->ssl, 0, sizeof(struct SslConn));
    -
    344 }
    -
    345
    -
    346 return true;
    -
    347#else
    -
    348 return false;
    -
    349#endif
    -
    350}
    -
    351
    -
    352/**
    -
    353 * qhttpclient->settimeout(): Sets connection wait timeout.
    -
    354 *
    -
    355 * @param client qhttpclient object pointer
    -
    356 * @param timeoutms timeout mili-seconds. 0 for system defaults
    -
    357 *
    -
    358 * @code
    -
    359 * httpclient->settimeout(httpclient, 0); // default
    -
    360 * httpclient->settimeout(httpclient, 5000); // 5 seconds
    -
    361 * @endcode
    -
    362 */
    -
    363static void settimeout(qhttpclient_t *client, int timeoutms) {
    -
    364 if (timeoutms <= 0)
    -
    365 timeoutms = -1;
    -
    366 client->timeoutms = timeoutms;
    -
    367}
    -
    368
    -
    369/**
    -
    370 * qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
    -
    371 *
    -
    372 * @param client qhttpclient object pointer
    -
    373 * @param keepalive true to set keep-alive on, false to set keep-alive off
    -
    374 *
    -
    375 * @code
    -
    376 * httpclient->setkeepalive(httpclient, true); // keep-alive on
    -
    377 * httpclient->setkeepalive(httpclient, false); // keep-alive off
    -
    378 * @endcode
    -
    379 */
    -
    380static void setkeepalive(qhttpclient_t *client, bool keepalive) {
    -
    381 client->keepalive = keepalive;
    -
    382}
    -
    383
    -
    384/**
    -
    385 * qhttpclient->setuseragent(): Sets user-agent string.
    -
    386 *
    -
    387 * @param client qhttpclient object pointer
    -
    388 * @param useragent user-agent string
    -
    389 *
    -
    390 * @code
    -
    391 * httpclient->setuseragent(httpclient, "MyAgent/1.0");
    -
    392 * @endcode
    -
    393 */
    -
    394static void setuseragent(qhttpclient_t *client, const char *useragent) {
    -
    395 if (client->useragent != NULL)
    -
    396 free(client->useragent);
    -
    397 client->useragent = strdup(useragent);
    -
    398}
    -
    399
    -
    400/**
    -
    401 * qhttpclient->open(): Opens a connection to the remote host.
    -
    402 *
    -
    403 * @param client qhttpclient object pointer
    -
    404 *
    -
    405 * @return true if successful, otherwise returns false
    -
    406 *
    -
    407 * @note
    -
    408 * Don't need to open a connection unless you definitely need to do this,
    -
    409 * because qhttpclient open a connection automatically when it's needed.
    -
    410 * This function also can be used to veryfy a connection failure with remote
    -
    411 * host.
    -
    412 *
    -
    413 * @code
    -
    414 * if(httpclient->open(httpclient) == false) return;
    -
    415 * @endcode
    -
    416 */
    -
    417static bool open_(qhttpclient_t *client) {
    -
    418 if (client->socket >= 0) {
    -
    419 // check if connection is still alive
    -
    420 if (qio_wait_writable(client->socket, 0) > 0)
    -
    421 return true;
    -
    422 _close(client);
    -
    423 }
    -
    424
    -
    425 // create new socket
    -
    426 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    -
    427 if (sockfd < 0) {
    -
    428 DEBUG("sockfd creation failed.");
    -
    429 return false;
    -
    430 }
    -
    431
    -
    432 // set to non-block socket if timeout is set
    -
    433 int sockflag = 0;
    -
    434 if (client->timeoutms > 0) {
    -
    435 sockflag = fcntl(sockfd, F_GETFL, 0);
    -
    436 fcntl(sockfd, F_SETFL, sockflag | O_NONBLOCK);
    -
    437 }
    -
    438
    -
    439 // try to connect
    -
    440 int status = connect(sockfd, (struct sockaddr *) &client->addr,
    -
    441 sizeof(client->addr));
    -
    442 if (status < 0
    -
    443 && (errno != EINPROGRESS
    -
    444 || qio_wait_writable(sockfd, client->timeoutms) <= 0)) {
    -
    445 DEBUG("connection failed. (%d)", errno);
    -
    446 close(sockfd);
    -
    447 return false;
    -
    448 }
    -
    449
    -
    450 // restore to block socket
    -
    451 if (client->timeoutms > 0) {
    -
    452 fcntl(sockfd, F_SETFL, sockflag);
    -
    453 }
    -
    454
    -
    455 // store socket descriptor
    -
    456 client->socket = sockfd;
    -
    457
    -
    458 // set socket option
    -
    459 _set_socket_option(sockfd);
    -
    460
    -
    461#ifdef ENABLE_OPENSSL
    -
    462 // set SSL option
    -
    463 if (client->ssl != NULL) {
    -
    464 // get ssl context using SSL 2 or 3
    -
    465 struct SslConn *ssl = client->ssl;
    -
    466 ssl->ctx = SSL_CTX_new(SSLv23_client_method());
    -
    467 if (ssl->ctx == NULL) {
    -
    468 DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    -
    469 _close(client);
    -
    470 return false;
    -
    471 }
    -
    472
    -
    473 // get ssl handle
    -
    474 ssl->ssl = SSL_new(ssl->ctx);
    -
    475 if (ssl->ssl == NULL) {
    -
    476 DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    -
    477 _close(client);
    -
    478 return false;
    -
    479 }
    -
    480
    -
    481 // map ssl handle with socket
    -
    482 if (SSL_set_fd(ssl->ssl, client->socket) != 1) {
    -
    483 DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    -
    484 _close(client);
    -
    485 return false;
    -
    486 }
    -
    487
    -
    488 // set ssl to work in client mode
    -
    489 SSL_set_connect_state(ssl->ssl);
    -
    490
    -
    491#ifndef OPENSSL_NO_TLSEXT
    -
    492 // set server name indication extension for the handshake
    -
    493 ssl->ssl->tlsext_hostname = client->hostname;
    -
    494#endif
    -
    495
    -
    496 // do handshake
    -
    497 if (SSL_connect(ssl->ssl) != 1) {
    -
    498 DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    -
    499 _close(client);
    -
    500 return false;
    -
    501 }
    -
    502
    -
    503 DEBUG("ssl initialized");
    -
    504 }
    -
    505#endif /* ENABLE_OPENSSL */
    -
    506
    -
    507 return true;
    -
    508}
    -
    509
    -
    510/**
    -
    511 * qhttpclient->head(): Sends a HEAD request.
    -
    512 *
    -
    513 * @param client qhttpclient object pointer.
    -
    514 * @param uri URL encoded remote URI for downloading file.
    -
    515 * ("/path" or "http://.../path")
    -
    516 * @param rescode if not NULL, remote response code will be stored.
    -
    517 * (can be NULL)
    -
    518 * @param reqheaders qlisttbl_t pointer which contains additional user
    -
    519 * request headers. (can be NULL)
    -
    520 * @param resheaders qlisttbl_t pointer for storing response headers.
    -
    521 * (can be NULL)
    -
    522 *
    -
    523 * @return true if successful(got 200 response), otherwise returns false
    -
    524 *
    -
    525 * @code
    -
    526 * main() {
    -
    527 * // create new HTTP client
    -
    528 * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    -
    529 * if(httpclient == NULL) return;
    -
    530 *
    -
    531 * // set additional custom headers
    -
    532 * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    533 * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    534 *
    -
    535 * // send HEAD request
    -
    536 * int nRescode = 0;
    -
    537 * char *pszEncPath = qEncodeUrl("/img/qdecoder.png");
    -
    538 * bool bRet = httpclient->head(httpclient, pszEncPath, &nRescode,
    -
    539 * reqheaders, resheaders);
    -
    540 * free(pszEncPath);
    -
    541 *
    -
    542 * // to print out request, response headers
    -
    543 * reqheaders->debug(reqheaders, stdout);
    -
    544 * resheaders->debug(resheaders, stdout);
    -
    545 *
    -
    546 * // check results
    -
    547 * if(bRet == false) {
    -
    548 * ...(error occured)...
    -
    549 * }
    -
    550 *
    -
    551 * // free resources
    -
    552 * httpclient->free(httpclient);
    -
    553 * reqheaders->free(reqheaders);
    -
    554 * resheaders->free(resheaders);
    -
    555 * }
    -
    556 * @endcode
    -
    557 */
    -
    558static bool head(qhttpclient_t *client, const char *uri, int *rescode,
    -
    559 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
    -
    560
    -
    561 // reset rescode
    -
    562 if (rescode != NULL)
    -
    563 *rescode = 0;
    -
    564
    -
    565 // generate request headers if necessary
    -
    566 bool freeReqHeaders = false;
    -
    567 if (reqheaders == NULL) {
    -
    568 reqheaders = qlisttbl(
    -
    569 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    570 freeReqHeaders = true;
    -
    571 }
    -
    572
    -
    573 // add additional headers
    -
    574 reqheaders->putstr(reqheaders, "Accept", "*/*");
    -
    575
    -
    576 // send request
    -
    577 bool sendret = sendrequest(client, "HEAD", uri, reqheaders);
    -
    578 if (freeReqHeaders == true)
    -
    579 reqheaders->free(reqheaders);
    -
    580 if (sendret == false) {
    -
    581 _close(client);
    -
    582 return false;
    -
    583 }
    -
    584
    -
    585 // read response
    -
    586 off_t clength = 0;
    -
    587 int resno = readresponse(client, resheaders, &clength);
    -
    588 if (rescode != NULL)
    -
    589 *rescode = resno;
    -
    590
    -
    591 // throw out content
    -
    592 if (clength > 0) {
    -
    593 if (read_(client, NULL, clength) != clength) {
    -
    594 _close(client);
    -
    595 }
    -
    596 }
    -
    597
    -
    598 // close connection if required
    -
    599 if (client->keepalive == false || client->connclose == true) {
    -
    600 _close(client);
    -
    601 }
    -
    602
    -
    603 if (resno == HTTP_CODE_OK)
    -
    604 return true;
    -
    605 return false;
    -
    606}
    -
    607
    -
    608/**
    -
    609 * qhttpclient->get(): Downloads a file from the remote host using GET
    -
    610 * method.
    -
    611 *
    -
    612 * @param client qhttpclient object pointer.
    -
    613 * @param uri URL encoded remote URI for downloading file.
    -
    614 * ("/path" or "http://.../path")
    -
    615 * @param fd opened file descriptor for writing.
    -
    616 * @param savesize if not NULL, the length of stored bytes will be stored.
    -
    617 * (can be NULL)
    -
    618 * @param rescode if not NULL, remote response code will be stored.
    -
    619 * (can be NULL)
    -
    620 * @param reqheaders qlisttbl_t pointer which contains additional user
    -
    621 * request headers. (can be NULL)
    -
    622 * @param resheaders qlisttbl_t pointer for storing response headers.
    -
    623 * (can be NULL)
    -
    624 * @param callback set user call-back function. (can be NULL)
    -
    625 * @param userdata set user data for call-back. (can be NULL)
    -
    626 *
    -
    627 * @return true if successful(200 OK), otherwise returns false
    -
    628 *
    -
    629 * @code
    -
    630 * struct userdata {
    -
    631 * ...
    -
    632 * };
    -
    633 *
    -
    634 * static bool callback(void *userdata, off_t sentbytes) {
    -
    635 * struct userdata *pMydata = (struct userdata*)userdata;
    -
    636 * ...(codes)...
    -
    637 * if(need_to_cancel) return false; // stop file uploading immediately
    -
    638 * return true;
    -
    639 * }
    -
    640 *
    -
    641 * main() {
    -
    642 * // create new HTTP client
    -
    643 * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    -
    644 * if(httpclient == NULL) return;
    -
    645 *
    -
    646 * // open file
    -
    647 * int nFd = open("/tmp/test.data", O_WRONLY | O_CREAT, 0644);
    -
    648 *
    -
    649 * // set additional custom headers
    -
    650 * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    651 * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    652 *
    -
    653 * // set userdata
    -
    654 * struct userdata mydata;
    -
    655 * ...(codes)...
    -
    656 *
    -
    657 * // send file
    -
    658 * int nRescode = 0;
    -
    659 * off_t nSavesize = 0;
    -
    660 * char *pszEncPath = qEncodeUrl("/img/qdecoder.png");
    -
    661 * bool bRet = httpclient->get(httpclient, pszEncPath, nFd, &nSavesize,
    -
    662 * &nRescode,
    -
    663 * reqheaders, resheaders,
    -
    664 * callback, (void*)&mydata);
    -
    665 * free(pszEncPath);
    -
    666 *
    -
    667 * // to print out request, response headers
    -
    668 * reqheaders->debug(reqheaders, stdout);
    -
    669 * resheaders->debug(resheaders, stdout);
    -
    670 *
    -
    671 * // check results
    -
    672 * if(bRet == false) {
    -
    673 * ...(error occured)...
    -
    674 * }
    -
    675 *
    -
    676 * // free resources
    -
    677 * httpclient->free(httpclient);
    -
    678 * reqheaders->free(reqheaders);
    -
    679 * resheaders->free(resheaders);
    -
    680 * close(nFd);
    -
    681 * }
    -
    682 * @endcode
    -
    683 *
    -
    684 * @note
    -
    685 * The call-back function will be called peridically whenever it send data as
    -
    686 * much as MAX_ATOMIC_DATA_SIZE. To stop uploading, return false in the
    -
    687 * call-back function, then PUT process will be stopped immediately.
    -
    688 * If a connection was not opened, it will open a connection automatically.
    -
    689 *
    -
    690 * @note
    -
    691 * The "rescode" will be set if it received any response code from a remote
    -
    692 * server even though it returns false.
    -
    693 */
    -
    694static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize,
    -
    695 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    -
    696 bool (*callback)(void *userdata, off_t recvbytes),
    -
    697 void *userdata) {
    -
    698
    -
    699 // reset rescode
    -
    700 if (rescode != NULL)
    -
    701 *rescode = 0;
    -
    702 if (savesize != NULL)
    -
    703 *savesize = 0;
    -
    704
    -
    705 // generate request headers if necessary
    -
    706 bool freeReqHeaders = false;
    -
    707 if (reqheaders == NULL) {
    -
    708 reqheaders = qlisttbl(
    -
    709 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    710 freeReqHeaders = true;
    -
    711 }
    -
    712
    -
    713 // add additional headers
    -
    714 reqheaders->putstr(reqheaders, "Accept", "*/*");
    -
    715
    -
    716 // send request
    -
    717 bool sendret = sendrequest(client, "GET", uri, reqheaders);
    -
    718 if (freeReqHeaders == true)
    -
    719 reqheaders->free(reqheaders);
    -
    720 if (sendret == false) {
    -
    721 _close(client);
    -
    722 return false;
    -
    723 }
    -
    724
    -
    725 // read response
    -
    726 off_t clength = 0;
    -
    727 int resno = readresponse(client, resheaders, &clength);
    -
    728 if (rescode != NULL)
    -
    729 *rescode = resno;
    -
    730
    -
    731 // check response code
    -
    732 if (resno != HTTP_CODE_OK) {
    -
    733 // throw out content
    -
    734 if (clength > 0) {
    -
    735 if (read_(client, NULL, clength) != clength) {
    -
    736 _close(client);
    -
    737 }
    -
    738 }
    -
    739
    -
    740 // close connection if required
    -
    741 if (client->keepalive == false || client->connclose == true) {
    -
    742 _close(client);
    -
    743 }
    -
    744 return false;
    -
    745 }
    -
    746
    -
    747 // start retrieving data
    -
    748 off_t recv = 0;
    -
    749 if (callback != NULL && callback(userdata, recv) == false) {
    -
    750 _close(client);
    -
    751 return false;
    -
    752 }
    -
    753
    -
    754 if (clength > 0) {
    -
    755 while (recv < clength) {
    -
    756 unsigned int recvsize; // this time receive size
    -
    757 if (clength - recv < MAX_ATOMIC_DATA_SIZE) {
    -
    758 recvsize = clength - recv;
    -
    759 } else {
    -
    760 recvsize = MAX_ATOMIC_DATA_SIZE;
    -
    761 }
    -
    762
    -
    763 ssize_t ret = recvfile(client, fd, recvsize);
    -
    764 if (ret <= 0)
    -
    765 break; // Connection closed by peer
    -
    766 recv += ret;
    -
    767 if (savesize != NULL)
    -
    768 *savesize = recv;
    -
    769
    -
    770 if (callback != NULL) {
    -
    771 if (callback(userdata, recv) == false) {
    -
    772 _close(client);
    -
    773 return false;
    -
    774 }
    -
    775 }
    -
    776 }
    -
    777
    -
    778 if (recv != clength) {
    -
    779 _close(client);
    -
    780 return false;
    -
    781 }
    -
    782
    -
    783 } else if (clength == -1) { // chunked
    -
    784 bool completed = false;
    -
    785 do {
    -
    786 // read chunk size
    -
    787 char buf[64];
    -
    788 if (gets_(client, buf, sizeof(buf)) <= 0)
    -
    789 break;
    -
    790
    -
    791 // parse chunk size
    -
    792 unsigned int recvsize; // this time chunk size
    -
    793 if (sscanf(buf, "%x", &recvsize) != 1) {
    -
    794 break;
    -
    795 }
    -
    796
    -
    797 if (recvsize == 0) {
    -
    798 // end of transfer
    -
    799 completed = true;
    -
    800 }
    -
    801
    -
    802 // save chunk
    -
    803 if (recvsize > 0) {
    -
    804 ssize_t ret = recvfile(client, fd, recvsize);
    -
    805 if (ret != recvsize)
    -
    806 break;
    -
    807 recv += ret;
    -
    808 DEBUG("%zd %zd", recv, ret);
    -
    809 if (savesize != NULL)
    -
    810 *savesize = recv;
    -
    811 }
    -
    812
    -
    813 // read tailing CRLF
    -
    814 if (gets_(client, buf, sizeof(buf)) <= 0)
    -
    815 break;
    -
    816
    -
    817 // call back
    -
    818 if (recvsize > 0 && callback != NULL
    -
    819 && callback(userdata, recv) == false) {
    -
    820 _close(client);
    -
    821 return false;
    -
    822 }
    -
    823 } while (completed == false);
    -
    824
    -
    825 if (completed == false) {
    -
    826 DEBUG("Broken pipe. %jd/chunked, errno=%d", recv, errno);
    -
    827 _close(client);
    -
    828 return false;
    -
    829 }
    -
    830 }
    -
    831
    -
    832 // close connection
    -
    833 if (client->keepalive == false || client->connclose == true) {
    -
    834 _close(client);
    -
    835 }
    -
    836
    -
    837 return true;
    -
    838}
    -
    839
    -
    840/**
    -
    841 * qhttpclient->put(): Uploads a file to the remote host using PUT method.
    -
    842 *
    -
    843 * @param client qhttpclient object pointer.
    -
    844 * @param uri remote URL for uploading file.
    -
    845 * ("/path" or "http://.../path")
    -
    846 * @param fd opened file descriptor for reading.
    -
    847 * @param length send size.
    -
    848 * @param rescode if not NULL, remote response code will be stored.
    -
    849 * (can be NULL)
    -
    850 * @param reqheaders qlisttbl_t pointer which contains additional user
    -
    851 * request headers. (can be NULL)
    -
    852 * @param resheaders qlisttbl_t pointer for storing response headers.
    -
    853 * (can be NULL)
    -
    854 * @param callback set user call-back function. (can be NULL)
    -
    855 * @param userdata set user data for call-back. (can be NULL)
    -
    856 *
    -
    857 * @return true if successful(201 Created), otherwise returns false
    -
    858 *
    -
    859 * @code
    -
    860 * struct userdata {
    -
    861 * ...
    -
    862 * };
    -
    863 *
    -
    864 * static bool callback(void *userdata, off_t sentbytes) {
    -
    865 * struct userdata *pMydata = (struct userdata*)userdata;
    -
    866 * ...(codes)...
    -
    867 * if(need_to_cancel) return false; // stop file uploading immediately
    -
    868 * return true;
    -
    869 * }
    -
    870 *
    -
    871 * main() {
    -
    872 * // create new HTTP client
    -
    873 * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    -
    874 * if(httpclient == NULL) return;
    -
    875 *
    -
    876 * // open file
    -
    877 * int nFd = open(...);
    -
    878 * off_t nFileSize = ...;
    -
    879 * char *pFileMd5sum = ...;
    -
    880 * time_t nFileDate = ...;
    -
    881 *
    -
    882 * // set additional custom headers
    -
    883 * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    884 * reqheaders->putstr(reqheaders, "X-FILE-MD5SUM", pFileMd5sum);
    -
    885 * reqheaders->putInt(reqheaders, "X-FILE-DATE", nFileDate);
    -
    886 *
    -
    887 * // set userdata
    -
    888 * struct userdata mydata;
    -
    889 * ...(codes)...
    -
    890 *
    -
    891 * // send file
    -
    892 * int nRescode = 0;
    -
    893 * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    894 * bool bRet = httpclient->put(httpclient,
    -
    895 * "/img/qdecoder.png", nFd, nFileSize,
    -
    896 * &nRescode,
    -
    897 * reqheaders, resheaders,
    -
    898 * callback, (void*)&mydata);
    -
    899 * // to print out request, response headers
    -
    900 * reqheaders->debug(reqheaders, stdout);
    -
    901 * resheaders->debug(resheaders, stdout);
    -
    902 *
    -
    903 * // check results
    -
    904 * if(bRet == false) {
    -
    905 * ...(error occured)...
    -
    906 * }
    -
    907 *
    -
    908 * // free resources
    -
    909 * httpclient->free(httpclient);
    -
    910 * reqheaders->free(reqheaders);
    -
    911 * resheaders->free(resheaders);
    -
    912 * close(nFd);
    -
    913 * }
    -
    914 * @endcode
    -
    915 *
    -
    916 * @note
    -
    917 * The call-back function will be called peridically whenever it send data as
    -
    918 * much as MAX_ATOMIC_DATA_SIZE. To stop uploading, return false in the
    -
    919 * call-back function, then PUT process will be stopped immediately.
    -
    920 * If a connection was not opened, it will open a connection automatically.
    -
    921 *
    -
    922 * @note
    -
    923 * The "rescode" will be set if it received any response code from a remote
    -
    924 * server even though it returns false.
    -
    925 */
    -
    926static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length,
    -
    927 int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    -
    928 bool (*callback)(void *userdata, off_t sentbytes),
    -
    929 void *userdata) {
    -
    930
    -
    931 // reset rescode
    -
    932 if (rescode != NULL)
    -
    933 *rescode = 0;
    -
    934
    -
    935 // generate request headers
    -
    936 bool freeReqHeaders = false;
    -
    937 if (reqheaders == NULL) {
    -
    938 reqheaders = qlisttbl(
    -
    939 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    940 freeReqHeaders = true;
    -
    941 }
    -
    942
    -
    943 // add additional headers
    -
    944 reqheaders->putstrf(reqheaders, "Content-Length", "%jd", length);
    -
    945 reqheaders->putstr(reqheaders, "Expect", "100-continue");
    -
    946
    -
    947 // send request
    -
    948 bool sendret = sendrequest(client, "PUT", uri, reqheaders);
    -
    949 if (freeReqHeaders == true) {
    -
    950 reqheaders->free(reqheaders);
    -
    951 reqheaders = NULL;
    -
    952 }
    -
    953 if (sendret == false) {
    -
    954 _close(client);
    -
    955 return false;
    -
    956 }
    -
    957
    -
    958 // wait 100-continue
    -
    959 if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    -
    960 DEBUG("timed out %d", client->timeoutms);
    -
    961 _close(client);
    -
    962 return false;
    -
    963 }
    -
    964
    -
    965 // read response
    -
    966 off_t clength = 0;
    -
    967 int resno = readresponse(client, resheaders, &clength);
    -
    968 if (resno != HTTP_CODE_CONTINUE) {
    -
    969 if (rescode != NULL)
    -
    970 *rescode = resno;
    -
    971
    -
    972 if (clength > 0) {
    -
    973 if (read_(client, NULL, clength) != clength) {
    -
    974 _close(client);
    -
    975 }
    -
    976 }
    -
    977
    -
    978 // close connection if required
    -
    979 if (client->keepalive == false || client->connclose == true) {
    -
    980 _close(client);
    -
    981 }
    -
    982 return false;
    -
    983 }
    -
    984
    -
    985 // send data
    -
    986 off_t sent = 0;
    -
    987 if (callback != NULL) {
    -
    988 if (callback(userdata, sent) == false) {
    -
    989 _close(client);
    -
    990 return false;
    -
    991 }
    -
    992 }
    -
    993 if (length > 0) {
    -
    994 while (sent < length) {
    -
    995 size_t sendsize; // this time sending size
    -
    996 if (length - sent < MAX_ATOMIC_DATA_SIZE)
    -
    997 sendsize = length - sent;
    -
    998 else
    -
    999 sendsize = MAX_ATOMIC_DATA_SIZE;
    -
    1000
    -
    1001 ssize_t ret = sendfile_(client, fd, sendsize);
    -
    1002 if (ret <= 0)
    -
    1003 break; // Connection closed by peer
    -
    1004 sent += ret;
    -
    1005
    -
    1006 if (callback != NULL) {
    -
    1007 if (callback(userdata, sent) == false) {
    -
    1008 _close(client);
    -
    1009 return false;
    -
    1010 }
    -
    1011 }
    -
    1012 }
    -
    1013
    -
    1014 if (sent != length) {
    -
    1015 _close(client);
    -
    1016 return false;
    -
    1017 }
    -
    1018
    -
    1019 if (callback != NULL) {
    -
    1020 if (callback(userdata, sent) == false) {
    -
    1021 _close(client);
    -
    1022 return false;
    -
    1023 }
    -
    1024 }
    -
    1025 }
    -
    1026
    -
    1027 // read response
    -
    1028 clength = 0;
    -
    1029 resno = readresponse(client, resheaders, &clength);
    -
    1030 if (rescode != NULL)
    -
    1031 *rescode = resno;
    -
    1032
    -
    1033 if (resno == HTTP_NO_RESPONSE) {
    -
    1034 _close(client);
    -
    1035 return false;
    -
    1036 }
    -
    1037
    -
    1038 if (clength > 0) {
    -
    1039 if (read_(client, NULL, clength) != clength) {
    -
    1040 _close(client);
    -
    1041 }
    -
    1042 }
    -
    1043
    -
    1044 // close connection
    -
    1045 if (client->keepalive == false || client->connclose == true) {
    -
    1046 _close(client);
    -
    1047 }
    -
    1048
    -
    1049 if (resno == HTTP_CODE_CREATED)
    -
    1050 return true;
    -
    1051 return false;
    -
    1052}
    -
    1053
    -
    1054/**
    -
    1055 * qhttpclient->cmd(): Sends a custom request(method) to the remote host
    -
    1056 * and reads it's response.
    -
    1057 *
    -
    1058 * @param client qhttpclient object pointer.
    -
    1059 * @param method method name.
    -
    1060 * @param uri remote URL for uploading file.
    -
    1061 * ("/path" or "http://.../path")
    -
    1062 * @param data data to send. (can be NULL)
    -
    1063 * @param size data size.
    -
    1064 * @param rescode if not NULL, remote response code will be stored.
    -
    1065 * (can be NULL)
    -
    1066 * @param contentslength if not NULL, the contents length will be stored.
    -
    1067 * (can be NULL)
    -
    1068 * @param reqheaders qlisttbl_t pointer which contains additional user
    -
    1069 * request headers. (can be NULL)
    -
    1070 * @param resheaders qlisttbl_t pointer for storing response headers.
    -
    1071 * (can be NULL)
    -
    1072 *
    -
    1073 * @return malloced content data if successful, otherwise returns NULL
    -
    1074 *
    -
    1075 * @code
    -
    1076 * int nResCode;
    -
    1077 * size_t nContentsLength;
    -
    1078 * void *contents = httpclient->cmd(httpclient, "DELETE" "/img/qdecoder.png",
    -
    1079 * NULL, 0
    -
    1080 * &nRescode, &nContentsLength
    -
    1081 * NULL, NULL);
    -
    1082 * if(contents == NULL) {
    -
    1083 * ...(error occured)...
    -
    1084 * } else {
    -
    1085 * printf("Response code : %d\n", nResCode);
    -
    1086 * printf("Contents length : %zu\n", nContentsLength);
    -
    1087 * printf("Contents : %s\n", (char*)contents); // if contents is printable
    -
    1088 * free(contents); // de-allocate
    -
    1089 * }
    -
    1090 * @endcode
    -
    1091 *
    -
    1092 * @note
    -
    1093 * This store server's response into memory so if you expect server responses
    -
    1094 * large amount of data, consider to use sendrequest() and readresponse()
    -
    1095 * instead of using this. The returning malloced content will be allocated
    -
    1096 * +1 byte than actual content size 'contentslength' and will be null
    -
    1097 * terminated.
    -
    1098 */
    -
    1099static void *cmd(qhttpclient_t *client, const char *method, const char *uri,
    -
    1100 void *data, size_t size, int *rescode, size_t *contentslength,
    -
    1101 qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
    -
    1102
    -
    1103 // reset rescode
    -
    1104 if (rescode != NULL)
    -
    1105 *rescode = 0;
    -
    1106 if (contentslength != NULL)
    -
    1107 *contentslength = 0;
    -
    1108
    -
    1109 // send request
    -
    1110 bool freeReqHeaders = false;
    -
    1111 if (reqheaders == NULL && data != NULL && size > 0) {
    -
    1112 reqheaders = qlisttbl(
    -
    1113 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    1114 reqheaders->putstrf(reqheaders, "Content-Length", "%jd", size);
    -
    1115 freeReqHeaders = true;
    -
    1116 }
    -
    1117
    -
    1118 bool sendret = sendrequest(client, method, uri, reqheaders);
    -
    1119 if (freeReqHeaders == true) {
    -
    1120 reqheaders->free(reqheaders);
    -
    1121 reqheaders = NULL;
    -
    1122 }
    -
    1123 if (sendret == false) {
    -
    1124 _close(client);
    -
    1125 return NULL;
    -
    1126 }
    -
    1127
    -
    1128 // send data
    -
    1129 if (data != NULL && size > 0) {
    -
    1130 ssize_t written = write_(client, data, size);
    -
    1131 if (written != size) {
    -
    1132 _close(client);
    -
    1133 return NULL;
    -
    1134 }
    -
    1135 }
    -
    1136
    -
    1137 // read response
    -
    1138 off_t clength = 0;
    -
    1139 int resno = readresponse(client, resheaders, &clength);
    -
    1140 if (rescode != NULL)
    -
    1141 *rescode = resno;
    -
    1142 if (contentslength != NULL)
    -
    1143 *contentslength = clength;
    -
    1144
    -
    1145 // malloc data
    -
    1146 void *content = NULL;
    -
    1147 if (clength > 0) {
    -
    1148 content = malloc(clength + 1);
    -
    1149 if (content != NULL) {
    -
    1150 if (read_(client, content, clength) == clength) {
    -
    1151 *(char *) (content + clength) = '\0';
    -
    1152 } else {
    -
    1153 free(content);
    -
    1154 content = NULL;
    -
    1155 _close(client);
    -
    1156 }
    -
    1157 }
    -
    1158 } else {
    -
    1159 // succeed. to distinguish between ok and error
    -
    1160 content = strdup("");
    -
    1161 }
    -
    1162
    -
    1163 // close connection
    -
    1164 if (client->keepalive == false || client->connclose == true) {
    -
    1165 _close(client);
    -
    1166 }
    -
    1167
    -
    1168 return content;
    -
    1169}
    -
    1170
    -
    1171/**
    -
    1172 * qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
    -
    1173 *
    -
    1174 * @param client qhttpclient object pointer
    -
    1175 * @param method HTTP method name
    -
    1176 * @param uri URI string for the method. ("/path" or "http://.../path")
    -
    1177 * @param reqheaders qlisttbl_t pointer which contains additional user
    -
    1178 * request headers. (can be NULL)
    -
    1179 *
    -
    1180 * @return true if successful, otherwise returns false
    -
    1181 *
    -
    1182 * @note
    -
    1183 * Default headers(Host, User-Agent, Connection) will be used if reqheaders
    -
    1184 * does not have those headers in it.
    -
    1185 *
    -
    1186 * @code
    -
    1187 * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    1188 * reqheaders->putstr(reqheaders, "Date", qTimeGetGmtStaticStr(0), true);
    -
    1189 *
    -
    1190 * httpclient->sendrequest(client,
    -
    1191 * "DELETE", "/img/qdecoder.png", reqheaders);
    -
    1192 * @endcode
    -
    1193 */
    -
    1194static bool sendrequest(qhttpclient_t *client, const char *method,
    -
    1195 const char *uri, qlisttbl_t *reqheaders) {
    -
    1196 if (open_(client) == false) {
    -
    1197 return false;
    -
    1198 }
    -
    1199
    -
    1200 // generate request headers if necessary
    -
    1201 bool freeReqHeaders = false;
    -
    1202 if (reqheaders == NULL) {
    -
    1203 reqheaders = qlisttbl(
    -
    1204 QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    1205 if (reqheaders == NULL)
    -
    1206 return false;
    -
    1207 freeReqHeaders = true;
    -
    1208 }
    -
    1209
    -
    1210 // append default headers
    -
    1211 if (reqheaders->get(reqheaders, "Host", NULL, false) == NULL) {
    -
    1212 reqheaders->putstrf(reqheaders, "Host", "%s:%d", client->hostname,
    -
    1213 client->port);
    -
    1214 }
    -
    1215 if (reqheaders->get(reqheaders, "User-Agent", NULL, false) == NULL) {
    -
    1216 reqheaders->putstr(reqheaders, "User-Agent", client->useragent);
    -
    1217 }
    -
    1218 if (reqheaders->get(reqheaders, "Connection", NULL, false) == NULL) {
    -
    1219 reqheaders->putstr(
    -
    1220 reqheaders, "Connection",
    -
    1221 (client->keepalive == true) ? "Keep-Alive" : "close");
    -
    1222 }
    -
    1223
    -
    1224 // create stream buffer
    -
    1225 qgrow_t *outBuf = qgrow(0);
    -
    1226 if (outBuf == NULL)
    -
    1227 return false;
    -
    1228
    -
    1229 // buffer out command
    -
    1230 outBuf->addstrf(outBuf, "%s %s %s\r\n", method, uri,
    -
    1231 HTTP_PROTOCOL_11);
    -
    1232
    -
    1233 // buffer out headers
    -
    1234 qlisttbl_obj_t obj;
    -
    1235 memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call
    -
    1236 reqheaders->lock(reqheaders);
    -
    1237 while (reqheaders->getnext(reqheaders, &obj, NULL, false) == true) {
    -
    1238 outBuf->addstrf(outBuf, "%s: %s\r\n", obj.name, (char *) obj.data);
    -
    1239 }
    -
    1240 reqheaders->unlock(reqheaders);
    -
    1241
    -
    1242 outBuf->addstrf(outBuf, "\r\n");
    -
    1243
    -
    1244 // stream out
    -
    1245 size_t towrite = 0;
    -
    1246 char *final = outBuf->toarray(outBuf, &towrite);
    -
    1247 ssize_t written = 0;
    -
    1248 if (final != NULL) {
    -
    1249 written = write_(client, final, towrite);
    -
    1250 free(final);
    -
    1251 }
    -
    1252
    -
    1253 // de-allocate
    -
    1254 outBuf->free(outBuf);
    -
    1255 if (freeReqHeaders == true)
    -
    1256 reqheaders->free(reqheaders);
    -
    1257
    -
    1258 if (written > 0 && written == towrite)
    -
    1259 return true;
    -
    1260 return false;
    -
    1261}
    -
    1262
    -
    1263/**
    -
    1264 * qhttpclient->readresponse(): Reads HTTP response header from the
    -
    1265 * remote host.
    -
    1266 *
    -
    1267 * @param client qhttpclient object pointer
    -
    1268 * @param resheaders qlisttbl_t pointer for storing response headers.
    -
    1269 * (can be NULL)
    -
    1270 * @param contentlength length of content body(or -1 for chunked transfer
    -
    1271 * encoding) will be stored. (can be NULL)
    -
    1272 *
    -
    1273 * @return numeric HTTP response code if successful, otherwise returns 0.
    -
    1274 *
    -
    1275 * @code
    -
    1276 * // send request
    -
    1277 * httpclient->sendrequest(client, "DELETE", "/img/qdecoder.png", NULL);
    -
    1278 *
    -
    1279 * // read response
    -
    1280 * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    -
    1281 * off_t clength;
    -
    1282 * int rescode = httpclient->readresponse(client, resheaders, &clength);
    -
    1283 * if(clength > 0) {
    -
    1284 * // read & throw out a content. don't need content
    -
    1285 * httpclient->read(client, NULL, clength);
    -
    1286 * }
    -
    1287 * @endcode
    -
    1288 *
    -
    1289 * @note
    -
    1290 * Data of content body must be read by a application side, if you want to use
    -
    1291 * Keep-Alive session. Please refer qhttpclient->read().
    -
    1292 */
    -
    1293static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
    -
    1294 off_t *contentlength) {
    -
    1295 if (contentlength != NULL) {
    -
    1296 *contentlength = 0;
    -
    1297 }
    -
    1298
    -
    1299 // read response
    -
    1300 char buf[1024];
    -
    1301 if (gets_(client, buf, sizeof(buf)) <= 0)
    -
    1302 return HTTP_NO_RESPONSE;
    -
    1303
    -
    1304 // parse response code
    -
    1305 if (strncmp(buf, "HTTP/", CONST_STRLEN("HTTP/")))
    -
    1306 return HTTP_NO_RESPONSE;
    -
    1307 char *tmp = strstr(buf, " ");
    -
    1308 if (tmp == NULL)
    -
    1309 return HTTP_NO_RESPONSE;
    -
    1310 int rescode = atoi(tmp + 1);
    -
    1311 if (rescode == 0)
    -
    1312 return HTTP_NO_RESPONSE;
    -
    1313
    -
    1314 // read headers
    -
    1315 while (gets_(client, buf, sizeof(buf)) > 0) {
    -
    1316 if (buf[0] == '\0')
    -
    1317 break;
    -
    1318
    -
    1319 // parse header
    -
    1320 char *name = buf;
    -
    1321 char *value = strstr(buf, ":");
    -
    1322 if (value != NULL) {
    -
    1323 *value = '\0';
    -
    1324 value += 1;
    -
    1325 qstrtrim(value);
    -
    1326 } else {
    -
    1327 // missing colon
    -
    1328 value = "";
    -
    1329 }
    -
    1330
    -
    1331 if (resheaders != NULL) {
    -
    1332 resheaders->putstr(resheaders, name, value);
    -
    1333 }
    -
    1334
    -
    1335 // check Connection header
    -
    1336 if (!strcasecmp(name, "Connection")) {
    -
    1337 if (!strcasecmp(value, "close")) {
    -
    1338 client->connclose = true;
    -
    1339 }
    -
    1340 }
    -
    1341 // check Content-Length & Transfer-Encoding header
    -
    1342 else if (contentlength != NULL && *contentlength == 0) {
    -
    1343 if (!strcasecmp(name, "Content-Length")) {
    -
    1344 *contentlength = atoll(value);
    -
    1345 }
    -
    1346 // check transfer-encoding header
    -
    1347 else if (!strcasecmp(name, "Transfer-Encoding")
    -
    1348 && !strcasecmp(value, "chunked")) {
    -
    1349 *contentlength = -1;
    -
    1350 }
    -
    1351 }
    -
    1352 }
    -
    1353
    -
    1354 return rescode;
    -
    1355}
    -
    1356
    -
    1357/**
    -
    1358 * qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
    -
    1359 *
    -
    1360 * @param client qhttpclient object pointer
    -
    1361 * @param buf data buffer pointer
    -
    1362 * @param bufsize buffer size
    -
    1363 *
    -
    1364 * @return the number of bytes read from file descriptor if successful,
    -
    1365 * otherwise returns -1.
    -
    1366 *
    -
    1367 * @note
    -
    1368 * Be sure the return value does not mean the length of actual stored data.
    -
    1369 * It means how many bytes are read from the file descriptor, so the new-line
    -
    1370 * characters will be counted, but not stored.
    -
    1371 */
    -
    1372static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize) {
    -
    1373#ifdef ENABLE_OPENSSL
    -
    1374 if (client->ssl == NULL) {
    -
    1375 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
    -
    1376 } else {
    -
    1377 if (bufsize <= 1) return -1;
    -
    1378
    -
    1379 struct SslConn *ssl = client->ssl;
    -
    1380 ssize_t readcnt = 0;
    -
    1381 char *ptr;
    -
    1382
    -
    1383 for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
    -
    1384 // wait readable
    -
    1385 //if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    -
    1386 // break;
    -
    1387 //}
    -
    1388
    -
    1389 int rsize = SSL_read(ssl->ssl, ptr, 1);
    -
    1390 if (rsize != 1) {
    -
    1391 unsigned long sslerr = ERR_get_error();
    -
    1392 if (sslerr == SSL_ERROR_WANT_READ) {
    -
    1393 continue;
    -
    1394 }
    -
    1395
    -
    1396 DEBUG("OpenSSL: %s (%d)",
    -
    1397 ERR_reason_error_string(sslerr), rsize);
    -
    1398 break;
    -
    1399 }
    -
    1400
    -
    1401 readcnt++;
    -
    1402 if (*ptr == '\r') ptr--;
    -
    1403 else if (*ptr == '\n') break;
    -
    1404 }
    -
    1405
    -
    1406 *ptr = '\0';
    -
    1407 DEBUG("SSL_read: %s (%zd)", buf, readcnt);
    -
    1408
    -
    1409 if (readcnt > 0) return readcnt;
    -
    1410 return -1;
    -
    1411 }
    -
    1412#else
    -
    1413 return qio_gets(client->socket, buf, bufsize, client->timeoutms);
    -
    1414#endif
    -
    1415}
    -
    1416
    -
    1417/**
    -
    1418 * qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
    -
    1419 *
    -
    1420 * @param client qhttpclient object pointer.
    -
    1421 * @param buf a buffer pointer for storing content. (can be NULL, then
    -
    1422 * read & throw out content)
    -
    1423 * @param length content size to read.
    -
    1424 *
    -
    1425 * @return number of bytes readed
    -
    1426 *
    -
    1427 * @code
    -
    1428 * off_t clength = 0;
    -
    1429 * int resno = client->readresponse(client, NULL, &clength);
    -
    1430 * if(clength > 0) {
    -
    1431 * void *buf = malloc(clength);
    -
    1432 * client->read(client, buf, clength);
    -
    1433 * }
    -
    1434 * @endcode
    -
    1435 */
    -
    1436static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes) {
    -
    1437#ifdef ENABLE_OPENSSL
    -
    1438 if (client->ssl == NULL) {
    -
    1439 return qio_read(client->socket, buf, nbytes, client->timeoutms);
    -
    1440 } else {
    -
    1441 if (nbytes == 0) return 0;
    -
    1442
    -
    1443 struct SslConn *ssl = client->ssl;
    -
    1444 ssize_t total = 0;
    -
    1445 while (total < nbytes) {
    -
    1446 //if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    -
    1447 // break;
    -
    1448 //}
    -
    1449
    -
    1450 int rsize = 0;
    -
    1451 if (buf != NULL) {
    -
    1452 rsize = SSL_read(ssl->ssl, buf + total, nbytes - total);
    -
    1453 } else {
    -
    1454 char trash[1024];
    -
    1455 int toread = nbytes - total;
    -
    1456 if (toread > sizeof(trash)) toread = sizeof(trash);
    -
    1457 rsize = SSL_read(ssl->ssl, trash, toread);
    -
    1458 }
    -
    1459 if (rsize <= 0) {
    -
    1460 DEBUG("OpenSSL: %s (%d)",
    -
    1461 ERR_reason_error_string(ERR_get_error()), rsize);
    -
    1462 unsigned long sslerr = ERR_get_error();
    -
    1463 if (sslerr == SSL_ERROR_WANT_READ) {
    -
    1464 usleep(1);
    -
    1465 continue;
    -
    1466 }
    -
    1467 break;
    -
    1468 }
    -
    1469 total += rsize;
    -
    1470 }
    -
    1471
    -
    1472 DEBUG("SSL_read: %zd", total);
    -
    1473 if (total > 0) return total;
    -
    1474 return -1;
    -
    1475 }
    -
    1476#else
    -
    1477 return qio_read(client->socket, buf, nbytes, client->timeoutms);
    -
    1478#endif
    -
    1479}
    -
    1480
    -
    1481/**
    -
    1482 * qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
    -
    1483 *
    -
    1484 * @param client qhttpclient object pointer.
    -
    1485 * @param buf a data pointer.
    -
    1486 * @param length content size to write.
    -
    1487 *
    -
    1488 * @return number of bytes written.
    -
    1489 */
    -
    1490static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes) {
    -
    1491#ifdef ENABLE_OPENSSL
    -
    1492 if (client->ssl == NULL) {
    -
    1493 return qio_write(client->socket, buf, nbytes, -1);
    -
    1494 } else {
    -
    1495 if (nbytes == 0) return 0;
    -
    1496
    -
    1497 struct SslConn *ssl = client->ssl;
    -
    1498 ssize_t total = 0;
    -
    1499 while (total < nbytes) {
    -
    1500 errno = 0;
    -
    1501 int wsize = SSL_write(ssl->ssl, buf + total, nbytes - total);
    -
    1502 if (wsize <= 0) {
    -
    1503 DEBUG("OpenSSL: %s (%d)",
    -
    1504 ERR_reason_error_string(ERR_get_error()), wsize);
    -
    1505 unsigned long sslerr = ERR_get_error();
    -
    1506 if (sslerr == SSL_ERROR_WANT_WRITE) {
    -
    1507 usleep(1);
    -
    1508 continue;
    -
    1509 }
    -
    1510 break;
    -
    1511 }
    -
    1512 total += wsize;
    -
    1513 }
    -
    1514
    -
    1515 DEBUG("SSL_write: %zd/%zu", total, nbytes);
    -
    1516 if (total > 0) return total;
    -
    1517 return -1;
    -
    1518 }
    -
    1519#else
    -
    1520 return qio_write(client->socket, buf, nbytes, -1);
    -
    1521#endif
    -
    1522}
    -
    1523
    -
    1524/**
    -
    1525 * qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save
    -
    1526 * into a file descriptor.
    -
    1527 *
    -
    1528 * @param client qhttpclient object pointer.
    -
    1529 * @param fd output file descriptor
    -
    1530 * @param nbytes the number of bytes to read and save.
    -
    1531 *
    -
    1532 * @return the number of bytes written if successful, otherwise returns -1.
    -
    1533 */
    -
    1534static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes) {
    -
    1535 if (nbytes == 0)
    -
    1536 return 0;
    -
    1537
    -
    1538 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
    -
    1539
    -
    1540 off_t total = 0; // total size sent
    -
    1541 while (total < nbytes) {
    -
    1542 size_t chunksize; // this time sending size
    -
    1543 if (nbytes - total <= sizeof(buf))
    -
    1544 chunksize = nbytes - total;
    -
    1545 else
    -
    1546 chunksize = sizeof(buf);
    -
    1547
    -
    1548 // read
    -
    1549 ssize_t rsize = read_(client, buf, chunksize);
    -
    1550 if (rsize <= 0)
    -
    1551 break;
    -
    1552
    -
    1553 // write
    -
    1554 ssize_t wsize = qio_write(fd, buf, rsize, -1);
    -
    1555 DEBUG("FILE write: %zd", wsize);
    -
    1556 if (wsize <= 0)
    -
    1557 break;
    -
    1558
    -
    1559 total += wsize;
    -
    1560 if (rsize != wsize) {
    -
    1561 DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    -
    1562 break;
    -
    1563 }
    -
    1564 }
    -
    1565
    -
    1566 if (total > 0)
    -
    1567 return total;
    -
    1568 return -1;
    -
    1569}
    -
    1570
    -
    1571/**
    -
    1572 * qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
    -
    1573 *
    -
    1574 * @param client qhttpclient object pointer.
    -
    1575 * @param fd input file descriptor
    -
    1576 * @param nbytes the number of bytes to read and send.
    -
    1577 *
    -
    1578 * @return the number of bytes sent if successful, otherwise returns -1.
    -
    1579 */
    -
    1580static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes) {
    -
    1581 if (nbytes == 0)
    -
    1582 return 0;
    -
    1583
    -
    1584 unsigned char buf[MAX_ATOMIC_DATA_SIZE];
    -
    1585
    -
    1586 off_t total = 0; // total size sent
    -
    1587 while (total < nbytes) {
    -
    1588 size_t chunksize; // this time sending size
    -
    1589 if (nbytes - total <= sizeof(buf))
    -
    1590 chunksize = nbytes - total;
    -
    1591 else
    -
    1592 chunksize = sizeof(buf);
    -
    1593
    -
    1594 // read
    -
    1595 ssize_t rsize = qio_read(fd, buf, chunksize, -1);
    -
    1596 DEBUG("FILE read: %zd", rsize);
    -
    1597 if (rsize <= 0)
    -
    1598 break;
    -
    1599
    -
    1600 // write
    -
    1601 ssize_t wsize = write_(client, buf, rsize);
    -
    1602 if (wsize <= 0)
    -
    1603 break;
    -
    1604
    -
    1605 total += wsize;
    -
    1606 if (rsize != wsize) {
    -
    1607 DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    -
    1608 break;
    -
    1609 }
    -
    1610 }
    -
    1611
    -
    1612 if (total > 0)
    -
    1613 return total;
    -
    1614 return -1;
    -
    1615}
    -
    1616
    -
    1617/**
    -
    1618 * qhttpclient->close(): Closes the connection.
    -
    1619 *
    -
    1620 * @param qhttpclient_t HTTP object pointer
    -
    1621 *
    -
    1622 * @return true if successful, otherwise returns false
    -
    1623 *
    -
    1624 * @code
    -
    1625 * httpclient->close(httpclient);
    -
    1626 * @endcode
    -
    1627 */
    -
    1628static bool _close(qhttpclient_t *client) {
    -
    1629 if (client->socket < 0)
    -
    1630 return false;
    -
    1631
    -
    1632#ifdef ENABLE_OPENSSL
    -
    1633 // release ssl connection
    -
    1634 if (client->ssl != NULL) {
    -
    1635 struct SslConn *ssl = client->ssl;
    -
    1636
    -
    1637 if (ssl->ssl != NULL) {
    -
    1638 SSL_shutdown(ssl->ssl);
    -
    1639 SSL_free(ssl->ssl);
    -
    1640 ssl->ssl = NULL;
    -
    1641 }
    -
    1642
    -
    1643 if (ssl->ctx != NULL) {
    -
    1644 SSL_CTX_free(ssl->ctx);
    -
    1645 ssl->ctx = NULL;
    -
    1646 }
    -
    1647 }
    -
    1648#endif
    -
    1649
    -
    1650 // shutdown connection
    -
    1651 if (client->ssl == NULL && MAX_SHUTDOWN_WAIT >= 0
    -
    1652 && shutdown(client->socket, SHUT_WR) == 0) {
    -
    1653 char buf[1024];
    -
    1654 while (qio_read(client->socket, buf, sizeof(buf), MAX_SHUTDOWN_WAIT) > 0);
    -
    1655 }
    -
    1656
    -
    1657 // close connection
    -
    1658 close(client->socket);
    -
    1659 client->socket = -1;
    -
    1660 client->connclose = false;
    -
    1661
    -
    1662 return true;
    -
    1663}
    -
    1664
    -
    1665/**
    -
    1666 * qhttpclient->free(): Free object.
    -
    1667 *
    -
    1668 * @param qhttpclient_t HTTP object pointer
    -
    1669 *
    -
    1670 * @note
    -
    1671 * If the connection was not closed, it will close the connection first prior
    -
    1672 * to de-allocate object.
    -
    1673 *
    -
    1674 * @code
    -
    1675 * httpclient->free(httpclient);
    -
    1676 * @endcode
    -
    1677 */
    -
    1678static void _free(qhttpclient_t *client) {
    -
    1679 if (client->socket >= 0) {
    -
    1680 client->close(client);
    -
    1681 }
    -
    1682
    -
    1683 if (client->ssl != NULL)
    -
    1684 free(client->ssl);
    -
    1685 if (client->hostname != NULL)
    -
    1686 free(client->hostname);
    -
    1687 if (client->useragent != NULL)
    -
    1688 free(client->useragent);
    -
    1689
    -
    1690 free(client);
    -
    1691}
    -
    1692
    -
    1693#ifndef _DOXYGEN_SKIP
    -
    1694static bool _set_socket_option(int socket) {
    -
    1695 bool ret = true;
    -
    1696
    -
    1697 // linger option
    -
    1698 if (SET_TCP_LINGER_TIMEOUT > 0) {
    -
    1699 struct linger li;
    -
    1700 li.l_onoff = 1;
    -
    1701 li.l_linger = SET_TCP_LINGER_TIMEOUT;
    -
    1702 if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &li,
    -
    1703 sizeof(struct linger)) < 0) {
    -
    1704 ret = false;
    -
    1705 }
    -
    1706 }
    -
    1707
    -
    1708 // nodelay option
    -
    1709 if (SET_TCP_NODELAY > 0) {
    -
    1710 int so_tcpnodelay = 1;
    -
    1711 if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &so_tcpnodelay,
    -
    1712 sizeof(so_tcpnodelay)) < 0) {
    -
    1713 ret = false;
    -
    1714 }
    -
    1715 }
    -
    1716
    -
    1717 return ret;
    -
    1718}
    -
    1719
    -
    1720static bool _parse_uri(const char *uri, bool *protocol, char *hostname,
    -
    1721 size_t namesize, int *port) {
    -
    1722
    -
    1723 if (!strncasecmp(uri, "http://", CONST_STRLEN("http://"))) {
    -
    1724 *protocol = false;
    -
    1725 *port = 80;
    -
    1726 } else if (!strncasecmp(uri, "https://", CONST_STRLEN("https://"))) {
    -
    1727 *protocol = true;
    -
    1728 *port = 443;
    -
    1729 } else {
    -
    1730 return false;
    -
    1731 }
    -
    1732
    -
    1733 char *t1 = strstr(uri, "://");
    -
    1734 t1 += 3;
    -
    1735 char *t2 = strstr(t1, "/");
    -
    1736 if (t2 == NULL)
    -
    1737 t2 = (char *) uri + strlen(uri);
    -
    1738
    -
    1739 if (t2 - t1 + 1 > namesize)
    -
    1740 return false;
    -
    1741 qstrncpy(hostname, namesize, t1, t2 - t1);
    -
    1742
    -
    1743 t1 = strstr(hostname, ":");
    -
    1744 if (t1 != NULL) {
    -
    1745 *t1 = '\0';
    -
    1746 *port = atoi(t1 + 1);
    -
    1747 }
    -
    1748
    -
    1749 return true;
    -
    1750}
    -
    1751#endif /* _DOXYGEN_SKIP */
    -
    1752
    -
    1753#endif /* DISABLE_QHTTPCLIENT */
    -
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition qgrow.c:134
    -
    static void settimeout(qhttpclient_t *client, int timeoutms)
    qhttpclient->settimeout(): Sets connection wait timeout.
    -
    static bool sendrequest(qhttpclient_t *client, const char *method, const char *uri, qlisttbl_t *reqheaders)
    qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
    -
    static void * cmd(qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.
    -
    static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes)
    qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save into a file descriptor.
    -
    static bool setssl(qhttpclient_t *client)
    qhttpclient->setssl(): Sets connection to HTTPS connection
    -
    static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes)
    qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
    -
    static bool _close(qhttpclient_t *client)
    qhttpclient->close(): Closes the connection.
    -
    static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes)
    qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
    -
    static void _free(qhttpclient_t *client)
    qhttpclient->free(): Free object.
    -
    static void setkeepalive(qhttpclient_t *client, bool keepalive)
    qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
    -
    static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders, off_t *contentlength)
    qhttpclient->readresponse(): Reads HTTP response header from the remote host.
    -
    qhttpclient_t * qhttpclient(const char *destname, int port)
    Initialize & create new HTTP client.
    -
    static bool open_(qhttpclient_t *client)
    qhttpclient->open(): Opens a connection to the remote host.
    -
    static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->head(): Sends a HEAD request.
    -
    static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes)
    qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
    -
    static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize)
    qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
    -
    static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
    qhttpclient->put(): Uploads a file to the remote host using PUT method.
    -
    static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
    qhttpclient->get(): Downloads a file from the remote host using GET method.
    -
    static void setuseragent(qhttpclient_t *client, const char *useragent)
    qhttpclient->setuseragent(): Sets user-agent string.
    -
    ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
    Write to a file descriptor.
    Definition qio.c:159
    -
    int qio_wait_readable(int fd, int timeoutms)
    Test & wait until the file descriptor has readable data.
    Definition qio.c:59
    -
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition qio.c:118
    -
    ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
    Read a line from a file descriptor into the buffer pointed to until either a terminating newline or E...
    Definition qio.c:257
    -
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition qio.c:87
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    -
    bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
    Convert hostname to sockaddr_in structure.
    Definition qsocket.c:135
    -
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition qstring.c:325
    -
    char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
    Copy src string to dst no more than n bytes.
    Definition qstring.c:344
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qhttpclient.c HTTP client object.
    +
    31  *
    +
    32  * qhttpclient implements HTTP client.
    +
    33  *
    +
    34  * Example code for simple HTTP GET operation.
    +
    35  *
    +
    36  * @code
    +
    37  * #define REMOTE_URL "/robots.txt"
    +
    38  * #define SAVEFILE "/tmp/robots.txt"
    +
    39  *
    +
    40  * int main(void) {
    +
    41  * // create new HTTP client
    +
    42  * qhttpclient_t *httpclient = qhttpclient("https://secure.qdecoder.org", 0);
    +
    43  * if(httpclient == NULL) return -1;
    +
    44  *
    +
    45  * // open file for writing
    +
    46  * int nFd = open(SAVEFILE, O_CREAT | O_TRUNC | O_WRONLY, 0644);
    +
    47  * if(nFd < 0) {
    +
    48  * httpclient->free(httpclient);
    +
    49  * return -1;
    +
    50  * }
    +
    51  *
    +
    52  * // container for storing response headers for debugging purpose
    +
    53  * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    54  *
    +
    55  * // download
    +
    56  * off_t nSavesize = 0;
    +
    57  * int nRescode = 0;
    +
    58  * bool bRet = httpclient->get(httpclient, REMOTE_URL, nFd, &nSavesize,
    +
    59  * &nRescode, NULL, resheaders, NULL, NULL);
    +
    60  *
    +
    61  * // close file
    +
    62  * close(nFd);
    +
    63  *
    +
    64  * // print out debugging info
    +
    65  * printf("%s %d, %d bytes saved\n", (bRet?"Success":"Failed"), nRescode,
    +
    66  * (int)nSavesize);
    +
    67  * resheaders->debug(resheaders, stdout);
    +
    68  *
    +
    69  * // de-allocate HTTP client object
    +
    70  * httpclient->free(httpclient);
    +
    71  *
    +
    72  * return (bRet ? 0 : -1);
    +
    73  * }
    +
    74  *
    +
    75  * [Output]
    +
    76  * Success 200, 30 bytes saved
    +
    77  * Date=Fri, 11 Feb 2011 23:40:50 GMT? (30)
    +
    78  * Server=Apache? (7)
    +
    79  * Last-Modified=Sun, 15 Mar 2009 11:43:07 GMT? (30)
    +
    80  * ETag="2e5c9d-1e-46526d665c8c0"? (26)
    +
    81  * Accept-Ranges=bytes? (6)
    +
    82  * Content-Length=30? (3)
    +
    83  * Cache-Control=max-age=604800? (15)
    +
    84  * Expires=Fri, 18 Feb 2011 23:40:50 GMT? (30)
    +
    85  * Connection=close? (6)
    +
    86  * Content-Type=text/plain? (11)
    +
    87  * @endcode
    +
    88  *
    +
    89  * Example code for multiple PUT operation using same keep-alive connection.
    +
    90  *
    +
    91  * @code
    +
    92  * // create new HTTP client
    +
    93  * qhttpclient_t *httpclient = qhttpclient("www.qdecoder.org", 80);
    +
    94  * if(httpclient == NULL) return;
    +
    95  *
    +
    96  * // set options
    +
    97  * httpclient->setkeepalive(httpclient, true);
    +
    98  *
    +
    99  * // make a connection
    +
    100  * if(httpclient->open(httpclient) == false) return;
    +
    101  *
    +
    102  * // upload files
    +
    103  * httpclient->put(httpclient, ...);
    +
    104  * httpclient->put(httpclient, ...); // will be done within same connection.
    +
    105  *
    +
    106  * // close connection - not necessary if we call free() just after this.
    +
    107  * httpclient->close(httpclient);
    +
    108  *
    +
    109  * // de-allocate HTTP client object
    +
    110  * httpclient->free(httpclient);
    +
    111  * @endcode
    +
    112  */
    +
    113 
    +
    114 #ifndef DISABLE_QHTTPCLIENT
    +
    115 
    +
    116 #include <stdio.h>
    +
    117 #include <stdlib.h>
    +
    118 #include <stdbool.h>
    +
    119 #include <string.h>
    +
    120 #include <unistd.h>
    +
    121 #include <fcntl.h>
    +
    122 #include <errno.h>
    +
    123 #include <sys/types.h>
    +
    124 #include <sys/socket.h>
    +
    125 #include <netinet/in.h>
    +
    126 #include <netinet/tcp.h>
    +
    127 #include <arpa/inet.h>
    +
    128 
    +
    129 #ifdef ENABLE_OPENSSL
    +
    130 #include "openssl/ssl.h"
    +
    131 #include "openssl/err.h"
    +
    132 #endif
    +
    133 
    +
    134 #include "qinternal.h"
    +
    135 #include "utilities/qio.h"
    +
    136 #include "utilities/qstring.h"
    +
    137 #include "utilities/qsocket.h"
    +
    138 #include "containers/qlisttbl.h"
    +
    139 #include "containers/qgrow.h"
    +
    140 #include "extensions/qhttpclient.h"
    +
    141 
    +
    142 #ifndef _DOXYGEN_SKIP
    +
    143 
    +
    144 static bool open_(qhttpclient_t *client);
    +
    145 static bool setssl(qhttpclient_t *client);
    +
    146 static void settimeout(qhttpclient_t *client, int timeoutms);
    +
    147 static void setkeepalive(qhttpclient_t *client, bool keepalive);
    +
    148 static void setuseragent(qhttpclient_t *client, const char *agentname);
    +
    149 
    +
    150 static bool head(qhttpclient_t *client, const char *uri, int *rescode,
    +
    151  qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
    +
    152 static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize,
    +
    153  int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    +
    154  bool (*callback)(void *userdata, off_t recvbytes),
    +
    155  void *userdata);
    +
    156 static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length,
    +
    157  int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    +
    158  bool (*callback)(void *userdata, off_t sentbytes),
    +
    159  void *userdata);
    +
    160 static void *cmd(qhttpclient_t *client, const char *method, const char *uri,
    +
    161  void *data, size_t size, int *rescode, size_t *contentslength,
    +
    162  qlisttbl_t *reqheaders, qlisttbl_t *resheaders);
    +
    163 
    +
    164 static bool sendrequest(qhttpclient_t *client, const char *method,
    +
    165  const char *uri, qlisttbl_t *reqheaders);
    +
    166 static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
    +
    167  off_t *contentlength);
    +
    168 
    +
    169 static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize);
    +
    170 static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes);
    +
    171 static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes);
    +
    172 static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes);
    +
    173 static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes);
    +
    174 
    +
    175 static bool _close(qhttpclient_t *client);
    +
    176 static void _free(qhttpclient_t *client);
    +
    177 
    +
    178 // internal usages
    +
    179 static bool _set_socket_option(int socket);
    +
    180 static bool _parse_uri(const char *uri, bool *protocol, char *hostname,
    +
    181  size_t namesize, int *port);
    +
    182 
    +
    183 #endif
    +
    184 
    +
    185 //
    +
    186 // HTTP RESPONSE CODE
    +
    187 //
    +
    188 #define HTTP_NO_RESPONSE (0)
    +
    189 #define HTTP_CODE_CONTINUE (100)
    +
    190 #define HTTP_CODE_OK (200)
    +
    191 #define HTTP_CODE_CREATED (201)
    +
    192 #define HTTP_CODE_NO_CONTENT (204)
    +
    193 #define HTTP_CODE_MULTI_STATUS (207)
    +
    194 #define HTTP_CODE_MOVED_TEMPORARILY (302)
    +
    195 #define HTTP_CODE_NOT_MODIFIED (304)
    +
    196 #define HTTP_CODE_BAD_REQUEST (400)
    +
    197 #define HTTP_CODE_FORBIDDEN (403)
    +
    198 #define HTTP_CODE_NOT_FOUND (404)
    +
    199 #define HTTP_CODE_METHOD_NOT_ALLOWED (405)
    +
    200 #define HTTP_CODE_REQUEST_TIME_OUT (408)
    +
    201 #define HTTP_CODE_REQUEST_URI_TOO_LONG (414)
    +
    202 #define HTTP_CODE_INTERNAL_SERVER_ERROR (500)
    +
    203 #define HTTP_CODE_NOT_IMPLEMENTED (501)
    +
    204 #define HTTP_CODE_SERVICE_UNAVAILABLE (503)
    +
    205 
    +
    206 #define HTTP_PROTOCOL_11 "HTTP/1.1"
    +
    207 
    +
    208 //
    +
    209 // TCP SOCKET DEFINITION
    +
    210 //
    +
    211 #define SET_TCP_LINGER_TIMEOUT (15) /*< linger seconds, 0 for disable */
    +
    212 #define SET_TCP_NODELAY (1) /*< 0 for disable */
    +
    213 #define MAX_SHUTDOWN_WAIT (100) /*< maximum shutdown wait, unit is ms */
    +
    214 #define MAX_ATOMIC_DATA_SIZE (32 * 1024) /*< maximum sending bytes */
    +
    215 
    +
    216 #ifdef ENABLE_OPENSSL
    +
    217 struct SslConn {
    +
    218  SSL *ssl;
    +
    219  SSL_CTX *ctx;
    +
    220 };
    +
    221 #endif
    +
    222 
    +
    223 /**
    +
    224  * Initialize & create new HTTP client.
    +
    225  *
    +
    226  * @param destname remote address, one of IP address, FQDN domain name and URI.
    +
    227  * @param port remote port number. (can be 0 when destname is URI)
    +
    228  *
    +
    229  * @return HTTP client object if succcessful, otherwise returns NULL.
    +
    230  *
    +
    231  * @code
    +
    232  * qhttpclient_t *client = qhttpclient("1.2.3.4", 80);
    +
    233  * qhttpclient_t *client = qhttpclient("www.qdecoder.org", 80);
    +
    234  * qhttpclient_t *client = qhttpclient("http://www.qdecoder.org", 0);
    +
    235  * qhttpclient_t *client = qhttpclient("http://www.qdecoder.org:80", 0);
    +
    236  * qhttpclient_t *client = qhttpclient("https://www.qdecoder.org", 0);
    +
    237  * qhttpclient_t *client = qhttpclient("https://www.qdecoder.org:443", 0);
    +
    238  * @endcode
    +
    239  *
    +
    240  * @note
    +
    241  * Keep-alive feature is turned off by default. Turn it on by calling
    +
    242  * setkeepalive(). If destname is URI string starting with
    +
    243  * "https://", setssl() will be called internally.
    +
    244  */
    +
    245 qhttpclient_t *qhttpclient(const char *destname, int port) {
    +
    246  bool ishttps = false;
    +
    247  char hostname[256];
    +
    248  if (port == 0 || strstr(destname, "://") != NULL) {
    +
    249  if (_parse_uri(destname, &ishttps, hostname, sizeof(hostname), &port)
    +
    250  == false) {
    +
    251  DEBUG("Can't parse URI %s", destname);
    +
    252  return NULL;
    +
    253  }
    +
    254 
    +
    255  DEBUG("https: %d, hostname: %s, port:%d\n", ishttps, hostname, port);
    +
    256  } else {
    +
    257  qstrcpy(hostname, sizeof(hostname), destname);
    +
    258  }
    +
    259 
    +
    260  // get remote address
    +
    261  struct sockaddr_in addr;
    +
    262  if (qsocket_get_addr(&addr, hostname, port) == false) {
    +
    263  return NULL;
    +
    264  }
    +
    265 
    +
    266  // allocate object
    +
    267  qhttpclient_t *client = (qhttpclient_t *) malloc(sizeof(qhttpclient_t));
    +
    268  if (client == NULL)
    +
    269  return NULL;
    +
    270  memset((void *) client, 0, sizeof(qhttpclient_t));
    +
    271 
    +
    272  // initialize object
    +
    273  client->socket = -1;
    +
    274 
    +
    275  memcpy((void *) &client->addr, (void *) &addr, sizeof(client->addr));
    +
    276  client->hostname = strdup(hostname);
    +
    277  client->port = port;
    +
    278 
    +
    279  // member methods
    +
    280  client->setssl = setssl;
    +
    281  client->settimeout = settimeout;
    +
    282  client->setkeepalive = setkeepalive;
    +
    283  client->setuseragent = setuseragent;
    +
    284 
    +
    285  client->open = open_;
    +
    286 
    +
    287  client->head = head;
    +
    288  client->get = get;
    +
    289  client->put = put;
    +
    290  client->cmd = cmd;
    +
    291 
    +
    292  client->sendrequest = sendrequest;
    +
    293  client->readresponse = readresponse;
    +
    294 
    +
    295  client->gets = gets_;
    +
    296  client->read = read_;
    +
    297  client->write = write_;
    +
    298  client->recvfile = recvfile;
    +
    299  client->sendfile = sendfile_;
    +
    300 
    +
    301  client->close = _close;
    +
    302  client->free = _free;
    +
    303 
    +
    304  // init client
    +
    305  settimeout(client, 0);
    +
    306  setkeepalive(client, false);
    +
    307  setuseragent(client, QHTTPCLIENT_NAME);
    +
    308  if (ishttps == true)
    +
    309  setssl(client);
    +
    310 
    +
    311  return client;
    +
    312 }
    +
    313 
    +
    314 /**
    +
    315  * qhttpclient->setssl(): Sets connection to HTTPS connection
    +
    316  *
    +
    317  * @param client qhttpclient object pointer
    +
    318  *
    +
    319  * @code
    +
    320  * httpclient->setssl(httpclient);
    +
    321  * @endcode
    +
    322  */
    +
    323 static bool setssl(qhttpclient_t *client) {
    +
    324 #ifdef ENABLE_OPENSSL
    +
    325  static bool initialized = false;
    +
    326 
    +
    327  if (client->socket >= 0) {
    +
    328  // must be set before making a connection.
    +
    329  return false;
    +
    330  }
    +
    331 
    +
    332  // init openssl
    +
    333  if (initialized == false) {
    +
    334  initialized = true;
    +
    335  SSL_load_error_strings();
    +
    336  SSL_library_init();
    +
    337  }
    +
    338 
    +
    339  // allocate ssl structure
    +
    340  if (client->ssl == NULL) {
    +
    341  client->ssl = malloc(sizeof(struct SslConn));
    +
    342  if (client->ssl == NULL) return false;
    +
    343  memset(client->ssl, 0, sizeof(struct SslConn));
    +
    344  }
    +
    345 
    +
    346  return true;
    +
    347 #else
    +
    348  return false;
    +
    349 #endif
    +
    350 }
    +
    351 
    +
    352 /**
    +
    353  * qhttpclient->settimeout(): Sets connection wait timeout.
    +
    354  *
    +
    355  * @param client qhttpclient object pointer
    +
    356  * @param timeoutms timeout mili-seconds. 0 for system defaults
    +
    357  *
    +
    358  * @code
    +
    359  * httpclient->settimeout(httpclient, 0); // default
    +
    360  * httpclient->settimeout(httpclient, 5000); // 5 seconds
    +
    361  * @endcode
    +
    362  */
    +
    363 static void settimeout(qhttpclient_t *client, int timeoutms) {
    +
    364  if (timeoutms <= 0)
    +
    365  timeoutms = -1;
    +
    366  client->timeoutms = timeoutms;
    +
    367 }
    +
    368 
    +
    369 /**
    +
    370  * qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
    +
    371  *
    +
    372  * @param client qhttpclient object pointer
    +
    373  * @param keepalive true to set keep-alive on, false to set keep-alive off
    +
    374  *
    +
    375  * @code
    +
    376  * httpclient->setkeepalive(httpclient, true); // keep-alive on
    +
    377  * httpclient->setkeepalive(httpclient, false); // keep-alive off
    +
    378  * @endcode
    +
    379  */
    +
    380 static void setkeepalive(qhttpclient_t *client, bool keepalive) {
    +
    381  client->keepalive = keepalive;
    +
    382 }
    +
    383 
    +
    384 /**
    +
    385  * qhttpclient->setuseragent(): Sets user-agent string.
    +
    386  *
    +
    387  * @param client qhttpclient object pointer
    +
    388  * @param useragent user-agent string
    +
    389  *
    +
    390  * @code
    +
    391  * httpclient->setuseragent(httpclient, "MyAgent/1.0");
    +
    392  * @endcode
    +
    393  */
    +
    394 static void setuseragent(qhttpclient_t *client, const char *useragent) {
    +
    395  if (client->useragent != NULL)
    +
    396  free(client->useragent);
    +
    397  client->useragent = strdup(useragent);
    +
    398 }
    +
    399 
    +
    400 /**
    +
    401  * qhttpclient->open(): Opens a connection to the remote host.
    +
    402  *
    +
    403  * @param client qhttpclient object pointer
    +
    404  *
    +
    405  * @return true if successful, otherwise returns false
    +
    406  *
    +
    407  * @note
    +
    408  * Don't need to open a connection unless you definitely need to do this,
    +
    409  * because qhttpclient open a connection automatically when it's needed.
    +
    410  * This function also can be used to veryfy a connection failure with remote
    +
    411  * host.
    +
    412  *
    +
    413  * @code
    +
    414  * if(httpclient->open(httpclient) == false) return;
    +
    415  * @endcode
    +
    416  */
    +
    417 static bool open_(qhttpclient_t *client) {
    +
    418  if (client->socket >= 0) {
    +
    419  // check if connection is still alive
    +
    420  if (qio_wait_writable(client->socket, 0) > 0)
    +
    421  return true;
    +
    422  _close(client);
    +
    423  }
    +
    424 
    +
    425  // create new socket
    +
    426  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    +
    427  if (sockfd < 0) {
    +
    428  DEBUG("sockfd creation failed.");
    +
    429  return false;
    +
    430  }
    +
    431 
    +
    432  // set to non-block socket if timeout is set
    +
    433  int sockflag = 0;
    +
    434  if (client->timeoutms > 0) {
    +
    435  sockflag = fcntl(sockfd, F_GETFL, 0);
    +
    436  fcntl(sockfd, F_SETFL, sockflag | O_NONBLOCK);
    +
    437  }
    +
    438 
    +
    439  // try to connect
    +
    440  int status = connect(sockfd, (struct sockaddr *) &client->addr,
    +
    441  sizeof(client->addr));
    +
    442  if (status < 0
    +
    443  && (errno != EINPROGRESS
    +
    444  || qio_wait_writable(sockfd, client->timeoutms) <= 0)) {
    +
    445  DEBUG("connection failed. (%d)", errno);
    +
    446  close(sockfd);
    +
    447  return false;
    +
    448  }
    +
    449 
    +
    450  // restore to block socket
    +
    451  if (client->timeoutms > 0) {
    +
    452  fcntl(sockfd, F_SETFL, sockflag);
    +
    453  }
    +
    454 
    +
    455  // store socket descriptor
    +
    456  client->socket = sockfd;
    +
    457 
    +
    458  // set socket option
    +
    459  _set_socket_option(sockfd);
    +
    460 
    +
    461 #ifdef ENABLE_OPENSSL
    +
    462  // set SSL option
    +
    463  if (client->ssl != NULL) {
    +
    464  // get ssl context using SSL 2 or 3
    +
    465  struct SslConn *ssl = client->ssl;
    +
    466  ssl->ctx = SSL_CTX_new(SSLv23_client_method());
    +
    467  if (ssl->ctx == NULL) {
    +
    468  DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    +
    469  _close(client);
    +
    470  return false;
    +
    471  }
    +
    472 
    +
    473  // get ssl handle
    +
    474  ssl->ssl = SSL_new(ssl->ctx);
    +
    475  if (ssl->ssl == NULL) {
    +
    476  DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    +
    477  _close(client);
    +
    478  return false;
    +
    479  }
    +
    480 
    +
    481  // map ssl handle with socket
    +
    482  if (SSL_set_fd(ssl->ssl, client->socket) != 1) {
    +
    483  DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    +
    484  _close(client);
    +
    485  return false;
    +
    486  }
    +
    487 
    +
    488  // set ssl to work in client mode
    +
    489  SSL_set_connect_state(ssl->ssl);
    +
    490 
    +
    491 #ifndef OPENSSL_NO_TLSEXT
    +
    492  // set server name indication extension for the handshake
    +
    493  ssl->ssl->tlsext_hostname = client->hostname;
    +
    494 #endif
    +
    495 
    +
    496  // do handshake
    +
    497  if (SSL_connect(ssl->ssl) != 1) {
    +
    498  DEBUG("OpenSSL: %s", ERR_reason_error_string(ERR_get_error()));
    +
    499  _close(client);
    +
    500  return false;
    +
    501  }
    +
    502 
    +
    503  DEBUG("ssl initialized");
    +
    504  }
    +
    505 #endif /* ENABLE_OPENSSL */
    +
    506 
    +
    507  return true;
    +
    508 }
    +
    509 
    +
    510 /**
    +
    511  * qhttpclient->head(): Sends a HEAD request.
    +
    512  *
    +
    513  * @param client qhttpclient object pointer.
    +
    514  * @param uri URL encoded remote URI for downloading file.
    +
    515  * ("/path" or "http://.../path")
    +
    516  * @param rescode if not NULL, remote response code will be stored.
    +
    517  * (can be NULL)
    +
    518  * @param reqheaders qlisttbl_t pointer which contains additional user
    +
    519  * request headers. (can be NULL)
    +
    520  * @param resheaders qlisttbl_t pointer for storing response headers.
    +
    521  * (can be NULL)
    +
    522  *
    +
    523  * @return true if successful(got 200 response), otherwise returns false
    +
    524  *
    +
    525  * @code
    +
    526  * main() {
    +
    527  * // create new HTTP client
    +
    528  * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    529  * if(httpclient == NULL) return;
    +
    530  *
    +
    531  * // set additional custom headers
    +
    532  * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    533  * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    534  *
    +
    535  * // send HEAD request
    +
    536  * int nRescode = 0;
    +
    537  * char *pszEncPath = qEncodeUrl("/img/qdecoder.png");
    +
    538  * bool bRet = httpclient->head(httpclient, pszEncPath, &nRescode,
    +
    539  * reqheaders, resheaders);
    +
    540  * free(pszEncPath);
    +
    541  *
    +
    542  * // to print out request, response headers
    +
    543  * reqheaders->debug(reqheaders, stdout);
    +
    544  * resheaders->debug(resheaders, stdout);
    +
    545  *
    +
    546  * // check results
    +
    547  * if(bRet == false) {
    +
    548  * ...(error occured)...
    +
    549  * }
    +
    550  *
    +
    551  * // free resources
    +
    552  * httpclient->free(httpclient);
    +
    553  * reqheaders->free(reqheaders);
    +
    554  * resheaders->free(resheaders);
    +
    555  * }
    +
    556  * @endcode
    +
    557  */
    +
    558 static bool head(qhttpclient_t *client, const char *uri, int *rescode,
    +
    559  qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
    +
    560 
    +
    561  // reset rescode
    +
    562  if (rescode != NULL)
    +
    563  *rescode = 0;
    +
    564 
    +
    565  // generate request headers if necessary
    +
    566  bool freeReqHeaders = false;
    +
    567  if (reqheaders == NULL) {
    +
    568  reqheaders = qlisttbl(
    +
    569  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    570  freeReqHeaders = true;
    +
    571  }
    +
    572 
    +
    573  // add additional headers
    +
    574  reqheaders->putstr(reqheaders, "Accept", "*/*");
    +
    575 
    +
    576  // send request
    +
    577  bool sendret = sendrequest(client, "HEAD", uri, reqheaders);
    +
    578  if (freeReqHeaders == true)
    +
    579  reqheaders->free(reqheaders);
    +
    580  if (sendret == false) {
    +
    581  _close(client);
    +
    582  return false;
    +
    583  }
    +
    584 
    +
    585  // read response
    +
    586  off_t clength = 0;
    +
    587  int resno = readresponse(client, resheaders, &clength);
    +
    588  if (rescode != NULL)
    +
    589  *rescode = resno;
    +
    590 
    +
    591  // throw out content
    +
    592  if (clength > 0) {
    +
    593  if (read_(client, NULL, clength) != clength) {
    +
    594  _close(client);
    +
    595  }
    +
    596  }
    +
    597 
    +
    598  // close connection if required
    +
    599  if (client->keepalive == false || client->connclose == true) {
    +
    600  _close(client);
    +
    601  }
    +
    602 
    +
    603  if (resno == HTTP_CODE_OK)
    +
    604  return true;
    +
    605  return false;
    +
    606 }
    +
    607 
    +
    608 /**
    +
    609  * qhttpclient->get(): Downloads a file from the remote host using GET
    +
    610  * method.
    +
    611  *
    +
    612  * @param client qhttpclient object pointer.
    +
    613  * @param uri URL encoded remote URI for downloading file.
    +
    614  * ("/path" or "http://.../path")
    +
    615  * @param fd opened file descriptor for writing.
    +
    616  * @param savesize if not NULL, the length of stored bytes will be stored.
    +
    617  * (can be NULL)
    +
    618  * @param rescode if not NULL, remote response code will be stored.
    +
    619  * (can be NULL)
    +
    620  * @param reqheaders qlisttbl_t pointer which contains additional user
    +
    621  * request headers. (can be NULL)
    +
    622  * @param resheaders qlisttbl_t pointer for storing response headers.
    +
    623  * (can be NULL)
    +
    624  * @param callback set user call-back function. (can be NULL)
    +
    625  * @param userdata set user data for call-back. (can be NULL)
    +
    626  *
    +
    627  * @return true if successful(200 OK), otherwise returns false
    +
    628  *
    +
    629  * @code
    +
    630  * struct userdata {
    +
    631  * ...
    +
    632  * };
    +
    633  *
    +
    634  * static bool callback(void *userdata, off_t sentbytes) {
    +
    635  * struct userdata *pMydata = (struct userdata*)userdata;
    +
    636  * ...(codes)...
    +
    637  * if(need_to_cancel) return false; // stop file uploading immediately
    +
    638  * return true;
    +
    639  * }
    +
    640  *
    +
    641  * main() {
    +
    642  * // create new HTTP client
    +
    643  * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    644  * if(httpclient == NULL) return;
    +
    645  *
    +
    646  * // open file
    +
    647  * int nFd = open("/tmp/test.data", O_WRONLY | O_CREAT, 0644);
    +
    648  *
    +
    649  * // set additional custom headers
    +
    650  * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    651  * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    652  *
    +
    653  * // set userdata
    +
    654  * struct userdata mydata;
    +
    655  * ...(codes)...
    +
    656  *
    +
    657  * // send file
    +
    658  * int nRescode = 0;
    +
    659  * off_t nSavesize = 0;
    +
    660  * char *pszEncPath = qEncodeUrl("/img/qdecoder.png");
    +
    661  * bool bRet = httpclient->get(httpclient, pszEncPath, nFd, &nSavesize,
    +
    662  * &nRescode,
    +
    663  * reqheaders, resheaders,
    +
    664  * callback, (void*)&mydata);
    +
    665  * free(pszEncPath);
    +
    666  *
    +
    667  * // to print out request, response headers
    +
    668  * reqheaders->debug(reqheaders, stdout);
    +
    669  * resheaders->debug(resheaders, stdout);
    +
    670  *
    +
    671  * // check results
    +
    672  * if(bRet == false) {
    +
    673  * ...(error occured)...
    +
    674  * }
    +
    675  *
    +
    676  * // free resources
    +
    677  * httpclient->free(httpclient);
    +
    678  * reqheaders->free(reqheaders);
    +
    679  * resheaders->free(resheaders);
    +
    680  * close(nFd);
    +
    681  * }
    +
    682  * @endcode
    +
    683  *
    +
    684  * @note
    +
    685  * The call-back function will be called peridically whenever it send data as
    +
    686  * much as MAX_ATOMIC_DATA_SIZE. To stop uploading, return false in the
    +
    687  * call-back function, then PUT process will be stopped immediately.
    +
    688  * If a connection was not opened, it will open a connection automatically.
    +
    689  *
    +
    690  * @note
    +
    691  * The "rescode" will be set if it received any response code from a remote
    +
    692  * server even though it returns false.
    +
    693  */
    +
    694 static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize,
    +
    695  int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    +
    696  bool (*callback)(void *userdata, off_t recvbytes),
    +
    697  void *userdata) {
    +
    698 
    +
    699  // reset rescode
    +
    700  if (rescode != NULL)
    +
    701  *rescode = 0;
    +
    702  if (savesize != NULL)
    +
    703  *savesize = 0;
    +
    704 
    +
    705  // generate request headers if necessary
    +
    706  bool freeReqHeaders = false;
    +
    707  if (reqheaders == NULL) {
    +
    708  reqheaders = qlisttbl(
    +
    709  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    710  freeReqHeaders = true;
    +
    711  }
    +
    712 
    +
    713  // add additional headers
    +
    714  reqheaders->putstr(reqheaders, "Accept", "*/*");
    +
    715 
    +
    716  // send request
    +
    717  bool sendret = sendrequest(client, "GET", uri, reqheaders);
    +
    718  if (freeReqHeaders == true)
    +
    719  reqheaders->free(reqheaders);
    +
    720  if (sendret == false) {
    +
    721  _close(client);
    +
    722  return false;
    +
    723  }
    +
    724 
    +
    725  // read response
    +
    726  off_t clength = 0;
    +
    727  int resno = readresponse(client, resheaders, &clength);
    +
    728  if (rescode != NULL)
    +
    729  *rescode = resno;
    +
    730 
    +
    731  // check response code
    +
    732  if (resno != HTTP_CODE_OK) {
    +
    733  // throw out content
    +
    734  if (clength > 0) {
    +
    735  if (read_(client, NULL, clength) != clength) {
    +
    736  _close(client);
    +
    737  }
    +
    738  }
    +
    739 
    +
    740  // close connection if required
    +
    741  if (client->keepalive == false || client->connclose == true) {
    +
    742  _close(client);
    +
    743  }
    +
    744  return false;
    +
    745  }
    +
    746 
    +
    747  // start retrieving data
    +
    748  off_t recv = 0;
    +
    749  if (callback != NULL && callback(userdata, recv) == false) {
    +
    750  _close(client);
    +
    751  return false;
    +
    752  }
    +
    753 
    +
    754  if (clength > 0) {
    +
    755  while (recv < clength) {
    +
    756  unsigned int recvsize; // this time receive size
    +
    757  if (clength - recv < MAX_ATOMIC_DATA_SIZE) {
    +
    758  recvsize = clength - recv;
    +
    759  } else {
    +
    760  recvsize = MAX_ATOMIC_DATA_SIZE;
    +
    761  }
    +
    762 
    +
    763  ssize_t ret = recvfile(client, fd, recvsize);
    +
    764  if (ret <= 0)
    +
    765  break; // Connection closed by peer
    +
    766  recv += ret;
    +
    767  if (savesize != NULL)
    +
    768  *savesize = recv;
    +
    769 
    +
    770  if (callback != NULL) {
    +
    771  if (callback(userdata, recv) == false) {
    +
    772  _close(client);
    +
    773  return false;
    +
    774  }
    +
    775  }
    +
    776  }
    +
    777 
    +
    778  if (recv != clength) {
    +
    779  _close(client);
    +
    780  return false;
    +
    781  }
    +
    782 
    +
    783  } else if (clength == -1) { // chunked
    +
    784  bool completed = false;
    +
    785  do {
    +
    786  // read chunk size
    +
    787  char buf[64];
    +
    788  if (gets_(client, buf, sizeof(buf)) <= 0)
    +
    789  break;
    +
    790 
    +
    791  // parse chunk size
    +
    792  unsigned int recvsize; // this time chunk size
    +
    793  if (sscanf(buf, "%x", &recvsize) != 1) {
    +
    794  break;
    +
    795  }
    +
    796 
    +
    797  if (recvsize == 0) {
    +
    798  // end of transfer
    +
    799  completed = true;
    +
    800  }
    +
    801 
    +
    802  // save chunk
    +
    803  if (recvsize > 0) {
    +
    804  ssize_t ret = recvfile(client, fd, recvsize);
    +
    805  if (ret != recvsize)
    +
    806  break;
    +
    807  recv += ret;
    +
    808  DEBUG("%zd %zd", recv, ret);
    +
    809  if (savesize != NULL)
    +
    810  *savesize = recv;
    +
    811  }
    +
    812 
    +
    813  // read tailing CRLF
    +
    814  if (gets_(client, buf, sizeof(buf)) <= 0)
    +
    815  break;
    +
    816 
    +
    817  // call back
    +
    818  if (recvsize > 0 && callback != NULL
    +
    819  && callback(userdata, recv) == false) {
    +
    820  _close(client);
    +
    821  return false;
    +
    822  }
    +
    823  } while (completed == false);
    +
    824 
    +
    825  if (completed == false) {
    +
    826  DEBUG("Broken pipe. %jd/chunked, errno=%d", recv, errno);
    +
    827  _close(client);
    +
    828  return false;
    +
    829  }
    +
    830  }
    +
    831 
    +
    832  // close connection
    +
    833  if (client->keepalive == false || client->connclose == true) {
    +
    834  _close(client);
    +
    835  }
    +
    836 
    +
    837  return true;
    +
    838 }
    +
    839 
    +
    840 /**
    +
    841  * qhttpclient->put(): Uploads a file to the remote host using PUT method.
    +
    842  *
    +
    843  * @param client qhttpclient object pointer.
    +
    844  * @param uri remote URL for uploading file.
    +
    845  * ("/path" or "http://.../path")
    +
    846  * @param fd opened file descriptor for reading.
    +
    847  * @param length send size.
    +
    848  * @param rescode if not NULL, remote response code will be stored.
    +
    849  * (can be NULL)
    +
    850  * @param reqheaders qlisttbl_t pointer which contains additional user
    +
    851  * request headers. (can be NULL)
    +
    852  * @param resheaders qlisttbl_t pointer for storing response headers.
    +
    853  * (can be NULL)
    +
    854  * @param callback set user call-back function. (can be NULL)
    +
    855  * @param userdata set user data for call-back. (can be NULL)
    +
    856  *
    +
    857  * @return true if successful(201 Created), otherwise returns false
    +
    858  *
    +
    859  * @code
    +
    860  * struct userdata {
    +
    861  * ...
    +
    862  * };
    +
    863  *
    +
    864  * static bool callback(void *userdata, off_t sentbytes) {
    +
    865  * struct userdata *pMydata = (struct userdata*)userdata;
    +
    866  * ...(codes)...
    +
    867  * if(need_to_cancel) return false; // stop file uploading immediately
    +
    868  * return true;
    +
    869  * }
    +
    870  *
    +
    871  * main() {
    +
    872  * // create new HTTP client
    +
    873  * qhttpclient_t *httpclient = qhttpclient("http://www.qdecoder.org", 0);
    +
    874  * if(httpclient == NULL) return;
    +
    875  *
    +
    876  * // open file
    +
    877  * int nFd = open(...);
    +
    878  * off_t nFileSize = ...;
    +
    879  * char *pFileMd5sum = ...;
    +
    880  * time_t nFileDate = ...;
    +
    881  *
    +
    882  * // set additional custom headers
    +
    883  * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    884  * reqheaders->putstr(reqheaders, "X-FILE-MD5SUM", pFileMd5sum);
    +
    885  * reqheaders->putInt(reqheaders, "X-FILE-DATE", nFileDate);
    +
    886  *
    +
    887  * // set userdata
    +
    888  * struct userdata mydata;
    +
    889  * ...(codes)...
    +
    890  *
    +
    891  * // send file
    +
    892  * int nRescode = 0;
    +
    893  * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    894  * bool bRet = httpclient->put(httpclient,
    +
    895  * "/img/qdecoder.png", nFd, nFileSize,
    +
    896  * &nRescode,
    +
    897  * reqheaders, resheaders,
    +
    898  * callback, (void*)&mydata);
    +
    899  * // to print out request, response headers
    +
    900  * reqheaders->debug(reqheaders, stdout);
    +
    901  * resheaders->debug(resheaders, stdout);
    +
    902  *
    +
    903  * // check results
    +
    904  * if(bRet == false) {
    +
    905  * ...(error occured)...
    +
    906  * }
    +
    907  *
    +
    908  * // free resources
    +
    909  * httpclient->free(httpclient);
    +
    910  * reqheaders->free(reqheaders);
    +
    911  * resheaders->free(resheaders);
    +
    912  * close(nFd);
    +
    913  * }
    +
    914  * @endcode
    +
    915  *
    +
    916  * @note
    +
    917  * The call-back function will be called peridically whenever it send data as
    +
    918  * much as MAX_ATOMIC_DATA_SIZE. To stop uploading, return false in the
    +
    919  * call-back function, then PUT process will be stopped immediately.
    +
    920  * If a connection was not opened, it will open a connection automatically.
    +
    921  *
    +
    922  * @note
    +
    923  * The "rescode" will be set if it received any response code from a remote
    +
    924  * server even though it returns false.
    +
    925  */
    +
    926 static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length,
    +
    927  int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders,
    +
    928  bool (*callback)(void *userdata, off_t sentbytes),
    +
    929  void *userdata) {
    +
    930 
    +
    931  // reset rescode
    +
    932  if (rescode != NULL)
    +
    933  *rescode = 0;
    +
    934 
    +
    935  // generate request headers
    +
    936  bool freeReqHeaders = false;
    +
    937  if (reqheaders == NULL) {
    +
    938  reqheaders = qlisttbl(
    +
    939  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    940  freeReqHeaders = true;
    +
    941  }
    +
    942 
    +
    943  // add additional headers
    +
    944  reqheaders->putstrf(reqheaders, "Content-Length", "%jd", length);
    +
    945  reqheaders->putstr(reqheaders, "Expect", "100-continue");
    +
    946 
    +
    947  // send request
    +
    948  bool sendret = sendrequest(client, "PUT", uri, reqheaders);
    +
    949  if (freeReqHeaders == true) {
    +
    950  reqheaders->free(reqheaders);
    +
    951  reqheaders = NULL;
    +
    952  }
    +
    953  if (sendret == false) {
    +
    954  _close(client);
    +
    955  return false;
    +
    956  }
    +
    957 
    +
    958  // wait 100-continue
    +
    959  if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    +
    960  DEBUG("timed out %d", client->timeoutms);
    +
    961  _close(client);
    +
    962  return false;
    +
    963  }
    +
    964 
    +
    965  // read response
    +
    966  off_t clength = 0;
    +
    967  int resno = readresponse(client, resheaders, &clength);
    +
    968  if (resno != HTTP_CODE_CONTINUE) {
    +
    969  if (rescode != NULL)
    +
    970  *rescode = resno;
    +
    971 
    +
    972  if (clength > 0) {
    +
    973  if (read_(client, NULL, clength) != clength) {
    +
    974  _close(client);
    +
    975  }
    +
    976  }
    +
    977 
    +
    978  // close connection if required
    +
    979  if (client->keepalive == false || client->connclose == true) {
    +
    980  _close(client);
    +
    981  }
    +
    982  return false;
    +
    983  }
    +
    984 
    +
    985  // send data
    +
    986  off_t sent = 0;
    +
    987  if (callback != NULL) {
    +
    988  if (callback(userdata, sent) == false) {
    +
    989  _close(client);
    +
    990  return false;
    +
    991  }
    +
    992  }
    +
    993  if (length > 0) {
    +
    994  while (sent < length) {
    +
    995  size_t sendsize; // this time sending size
    +
    996  if (length - sent < MAX_ATOMIC_DATA_SIZE)
    +
    997  sendsize = length - sent;
    +
    998  else
    +
    999  sendsize = MAX_ATOMIC_DATA_SIZE;
    +
    1000 
    +
    1001  ssize_t ret = sendfile_(client, fd, sendsize);
    +
    1002  if (ret <= 0)
    +
    1003  break; // Connection closed by peer
    +
    1004  sent += ret;
    +
    1005 
    +
    1006  if (callback != NULL) {
    +
    1007  if (callback(userdata, sent) == false) {
    +
    1008  _close(client);
    +
    1009  return false;
    +
    1010  }
    +
    1011  }
    +
    1012  }
    +
    1013 
    +
    1014  if (sent != length) {
    +
    1015  _close(client);
    +
    1016  return false;
    +
    1017  }
    +
    1018 
    +
    1019  if (callback != NULL) {
    +
    1020  if (callback(userdata, sent) == false) {
    +
    1021  _close(client);
    +
    1022  return false;
    +
    1023  }
    +
    1024  }
    +
    1025  }
    +
    1026 
    +
    1027  // read response
    +
    1028  clength = 0;
    +
    1029  resno = readresponse(client, resheaders, &clength);
    +
    1030  if (rescode != NULL)
    +
    1031  *rescode = resno;
    +
    1032 
    +
    1033  if (resno == HTTP_NO_RESPONSE) {
    +
    1034  _close(client);
    +
    1035  return false;
    +
    1036  }
    +
    1037 
    +
    1038  if (clength > 0) {
    +
    1039  if (read_(client, NULL, clength) != clength) {
    +
    1040  _close(client);
    +
    1041  }
    +
    1042  }
    +
    1043 
    +
    1044  // close connection
    +
    1045  if (client->keepalive == false || client->connclose == true) {
    +
    1046  _close(client);
    +
    1047  }
    +
    1048 
    +
    1049  if (resno == HTTP_CODE_CREATED)
    +
    1050  return true;
    +
    1051  return false;
    +
    1052 }
    +
    1053 
    +
    1054 /**
    +
    1055  * qhttpclient->cmd(): Sends a custom request(method) to the remote host
    +
    1056  * and reads it's response.
    +
    1057  *
    +
    1058  * @param client qhttpclient object pointer.
    +
    1059  * @param method method name.
    +
    1060  * @param uri remote URL for uploading file.
    +
    1061  * ("/path" or "http://.../path")
    +
    1062  * @param data data to send. (can be NULL)
    +
    1063  * @param size data size.
    +
    1064  * @param rescode if not NULL, remote response code will be stored.
    +
    1065  * (can be NULL)
    +
    1066  * @param contentslength if not NULL, the contents length will be stored.
    +
    1067  * (can be NULL)
    +
    1068  * @param reqheaders qlisttbl_t pointer which contains additional user
    +
    1069  * request headers. (can be NULL)
    +
    1070  * @param resheaders qlisttbl_t pointer for storing response headers.
    +
    1071  * (can be NULL)
    +
    1072  *
    +
    1073  * @return malloced content data if successful, otherwise returns NULL
    +
    1074  *
    +
    1075  * @code
    +
    1076  * int nResCode;
    +
    1077  * size_t nContentsLength;
    +
    1078  * void *contents = httpclient->cmd(httpclient, "DELETE" "/img/qdecoder.png",
    +
    1079  * NULL, 0
    +
    1080  * &nRescode, &nContentsLength
    +
    1081  * NULL, NULL);
    +
    1082  * if(contents == NULL) {
    +
    1083  * ...(error occured)...
    +
    1084  * } else {
    +
    1085  * printf("Response code : %d\n", nResCode);
    +
    1086  * printf("Contents length : %zu\n", nContentsLength);
    +
    1087  * printf("Contents : %s\n", (char*)contents); // if contents is printable
    +
    1088  * free(contents); // de-allocate
    +
    1089  * }
    +
    1090  * @endcode
    +
    1091  *
    +
    1092  * @note
    +
    1093  * This store server's response into memory so if you expect server responses
    +
    1094  * large amount of data, consider to use sendrequest() and readresponse()
    +
    1095  * instead of using this. The returning malloced content will be allocated
    +
    1096  * +1 byte than actual content size 'contentslength' and will be null
    +
    1097  * terminated.
    +
    1098  */
    +
    1099 static void *cmd(qhttpclient_t *client, const char *method, const char *uri,
    +
    1100  void *data, size_t size, int *rescode, size_t *contentslength,
    +
    1101  qlisttbl_t *reqheaders, qlisttbl_t *resheaders) {
    +
    1102 
    +
    1103  // reset rescode
    +
    1104  if (rescode != NULL)
    +
    1105  *rescode = 0;
    +
    1106  if (contentslength != NULL)
    +
    1107  *contentslength = 0;
    +
    1108 
    +
    1109  // send request
    +
    1110  bool freeReqHeaders = false;
    +
    1111  if (reqheaders == NULL && data != NULL && size > 0) {
    +
    1112  reqheaders = qlisttbl(
    +
    1113  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    1114  reqheaders->putstrf(reqheaders, "Content-Length", "%jd", size);
    +
    1115  freeReqHeaders = true;
    +
    1116  }
    +
    1117 
    +
    1118  bool sendret = sendrequest(client, method, uri, reqheaders);
    +
    1119  if (freeReqHeaders == true) {
    +
    1120  reqheaders->free(reqheaders);
    +
    1121  reqheaders = NULL;
    +
    1122  }
    +
    1123  if (sendret == false) {
    +
    1124  _close(client);
    +
    1125  return NULL;
    +
    1126  }
    +
    1127 
    +
    1128  // send data
    +
    1129  if (data != NULL && size > 0) {
    +
    1130  ssize_t written = write_(client, data, size);
    +
    1131  if (written != size) {
    +
    1132  _close(client);
    +
    1133  return NULL;
    +
    1134  }
    +
    1135  }
    +
    1136 
    +
    1137  // read response
    +
    1138  off_t clength = 0;
    +
    1139  int resno = readresponse(client, resheaders, &clength);
    +
    1140  if (rescode != NULL)
    +
    1141  *rescode = resno;
    +
    1142  if (contentslength != NULL)
    +
    1143  *contentslength = clength;
    +
    1144 
    +
    1145  // malloc data
    +
    1146  void *content = NULL;
    +
    1147  if (clength > 0) {
    +
    1148  content = malloc(clength + 1);
    +
    1149  if (content != NULL) {
    +
    1150  if (read_(client, content, clength) == clength) {
    +
    1151  *(char *) (content + clength) = '\0';
    +
    1152  } else {
    +
    1153  free(content);
    +
    1154  content = NULL;
    +
    1155  _close(client);
    +
    1156  }
    +
    1157  }
    +
    1158  } else {
    +
    1159  // succeed. to distinguish between ok and error
    +
    1160  content = strdup("");
    +
    1161  }
    +
    1162 
    +
    1163  // close connection
    +
    1164  if (client->keepalive == false || client->connclose == true) {
    +
    1165  _close(client);
    +
    1166  }
    +
    1167 
    +
    1168  return content;
    +
    1169 }
    +
    1170 
    +
    1171 /**
    +
    1172  * qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
    +
    1173  *
    +
    1174  * @param client qhttpclient object pointer
    +
    1175  * @param method HTTP method name
    +
    1176  * @param uri URI string for the method. ("/path" or "http://.../path")
    +
    1177  * @param reqheaders qlisttbl_t pointer which contains additional user
    +
    1178  * request headers. (can be NULL)
    +
    1179  *
    +
    1180  * @return true if successful, otherwise returns false
    +
    1181  *
    +
    1182  * @note
    +
    1183  * Default headers(Host, User-Agent, Connection) will be used if reqheaders
    +
    1184  * does not have those headers in it.
    +
    1185  *
    +
    1186  * @code
    +
    1187  * qlisttbl_t *reqheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    1188  * reqheaders->putstr(reqheaders, "Date", qTimeGetGmtStaticStr(0), true);
    +
    1189  *
    +
    1190  * httpclient->sendrequest(client,
    +
    1191  * "DELETE", "/img/qdecoder.png", reqheaders);
    +
    1192  * @endcode
    +
    1193  */
    +
    1194 static bool sendrequest(qhttpclient_t *client, const char *method,
    +
    1195  const char *uri, qlisttbl_t *reqheaders) {
    +
    1196  if (open_(client) == false) {
    +
    1197  return false;
    +
    1198  }
    +
    1199 
    +
    1200  // generate request headers if necessary
    +
    1201  bool freeReqHeaders = false;
    +
    1202  if (reqheaders == NULL) {
    +
    1203  reqheaders = qlisttbl(
    +
    1204  QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    1205  if (reqheaders == NULL)
    +
    1206  return false;
    +
    1207  freeReqHeaders = true;
    +
    1208  }
    +
    1209 
    +
    1210  // append default headers
    +
    1211  if (reqheaders->get(reqheaders, "Host", NULL, false) == NULL) {
    +
    1212  reqheaders->putstrf(reqheaders, "Host", "%s:%d", client->hostname,
    +
    1213  client->port);
    +
    1214  }
    +
    1215  if (reqheaders->get(reqheaders, "User-Agent", NULL, false) == NULL) {
    +
    1216  reqheaders->putstr(reqheaders, "User-Agent", client->useragent);
    +
    1217  }
    +
    1218  if (reqheaders->get(reqheaders, "Connection", NULL, false) == NULL) {
    +
    1219  reqheaders->putstr(
    +
    1220  reqheaders, "Connection",
    +
    1221  (client->keepalive == true) ? "Keep-Alive" : "close");
    +
    1222  }
    +
    1223 
    +
    1224  // create stream buffer
    +
    1225  qgrow_t *outBuf = qgrow(0);
    +
    1226  if (outBuf == NULL)
    +
    1227  return false;
    +
    1228 
    +
    1229  // buffer out command
    +
    1230  outBuf->addstrf(outBuf, "%s %s %s\r\n", method, uri,
    +
    1231  HTTP_PROTOCOL_11);
    +
    1232 
    +
    1233  // buffer out headers
    +
    1234  qlisttbl_obj_t obj;
    +
    1235  memset((void *) &obj, 0, sizeof(obj)); // must be cleared before call
    +
    1236  reqheaders->lock(reqheaders);
    +
    1237  while (reqheaders->getnext(reqheaders, &obj, NULL, false) == true) {
    +
    1238  outBuf->addstrf(outBuf, "%s: %s\r\n", obj.name, (char *) obj.data);
    +
    1239  }
    +
    1240  reqheaders->unlock(reqheaders);
    +
    1241 
    +
    1242  outBuf->addstrf(outBuf, "\r\n");
    +
    1243 
    +
    1244  // stream out
    +
    1245  size_t towrite = 0;
    +
    1246  char *final = outBuf->toarray(outBuf, &towrite);
    +
    1247  ssize_t written = 0;
    +
    1248  if (final != NULL) {
    +
    1249  written = write_(client, final, towrite);
    +
    1250  free(final);
    +
    1251  }
    +
    1252 
    +
    1253  // de-allocate
    +
    1254  outBuf->free(outBuf);
    +
    1255  if (freeReqHeaders == true)
    +
    1256  reqheaders->free(reqheaders);
    +
    1257 
    +
    1258  if (written > 0 && written == towrite)
    +
    1259  return true;
    +
    1260  return false;
    +
    1261 }
    +
    1262 
    +
    1263 /**
    +
    1264  * qhttpclient->readresponse(): Reads HTTP response header from the
    +
    1265  * remote host.
    +
    1266  *
    +
    1267  * @param client qhttpclient object pointer
    +
    1268  * @param resheaders qlisttbl_t pointer for storing response headers.
    +
    1269  * (can be NULL)
    +
    1270  * @param contentlength length of content body(or -1 for chunked transfer
    +
    1271  * encoding) will be stored. (can be NULL)
    +
    1272  *
    +
    1273  * @return numeric HTTP response code if successful, otherwise returns 0.
    +
    1274  *
    +
    1275  * @code
    +
    1276  * // send request
    +
    1277  * httpclient->sendrequest(client, "DELETE", "/img/qdecoder.png", NULL);
    +
    1278  *
    +
    1279  * // read response
    +
    1280  * qlisttbl_t *resheaders = qlisttbl(QLISTTBL_UNIQUE | QLISTTBL_CASEINSENSITIVE);
    +
    1281  * off_t clength;
    +
    1282  * int rescode = httpclient->readresponse(client, resheaders, &clength);
    +
    1283  * if(clength > 0) {
    +
    1284  * // read & throw out a content. don't need content
    +
    1285  * httpclient->read(client, NULL, clength);
    +
    1286  * }
    +
    1287  * @endcode
    +
    1288  *
    +
    1289  * @note
    +
    1290  * Data of content body must be read by a application side, if you want to use
    +
    1291  * Keep-Alive session. Please refer qhttpclient->read().
    +
    1292  */
    +
    1293 static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders,
    +
    1294  off_t *contentlength) {
    +
    1295  if (contentlength != NULL) {
    +
    1296  *contentlength = 0;
    +
    1297  }
    +
    1298 
    +
    1299  // read response
    +
    1300  char buf[1024];
    +
    1301  if (gets_(client, buf, sizeof(buf)) <= 0)
    +
    1302  return HTTP_NO_RESPONSE;
    +
    1303 
    +
    1304  // parse response code
    +
    1305  if (strncmp(buf, "HTTP/", CONST_STRLEN("HTTP/")))
    +
    1306  return HTTP_NO_RESPONSE;
    +
    1307  char *tmp = strstr(buf, " ");
    +
    1308  if (tmp == NULL)
    +
    1309  return HTTP_NO_RESPONSE;
    +
    1310  int rescode = atoi(tmp + 1);
    +
    1311  if (rescode == 0)
    +
    1312  return HTTP_NO_RESPONSE;
    +
    1313 
    +
    1314  // read headers
    +
    1315  while (gets_(client, buf, sizeof(buf)) > 0) {
    +
    1316  if (buf[0] == '\0')
    +
    1317  break;
    +
    1318 
    +
    1319  // parse header
    +
    1320  char *name = buf;
    +
    1321  char *value = strstr(buf, ":");
    +
    1322  if (value != NULL) {
    +
    1323  *value = '\0';
    +
    1324  value += 1;
    +
    1325  qstrtrim(value);
    +
    1326  } else {
    +
    1327  // missing colon
    +
    1328  value = "";
    +
    1329  }
    +
    1330 
    +
    1331  if (resheaders != NULL) {
    +
    1332  resheaders->putstr(resheaders, name, value);
    +
    1333  }
    +
    1334 
    +
    1335  // check Connection header
    +
    1336  if (!strcasecmp(name, "Connection")) {
    +
    1337  if (!strcasecmp(value, "close")) {
    +
    1338  client->connclose = true;
    +
    1339  }
    +
    1340  }
    +
    1341  // check Content-Length & Transfer-Encoding header
    +
    1342  else if (contentlength != NULL && *contentlength == 0) {
    +
    1343  if (!strcasecmp(name, "Content-Length")) {
    +
    1344  *contentlength = atoll(value);
    +
    1345  }
    +
    1346  // check transfer-encoding header
    +
    1347  else if (!strcasecmp(name, "Transfer-Encoding")
    +
    1348  && !strcasecmp(value, "chunked")) {
    +
    1349  *contentlength = -1;
    +
    1350  }
    +
    1351  }
    +
    1352  }
    +
    1353 
    +
    1354  return rescode;
    +
    1355 }
    +
    1356 
    +
    1357 /**
    +
    1358  * qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
    +
    1359  *
    +
    1360  * @param client qhttpclient object pointer
    +
    1361  * @param buf data buffer pointer
    +
    1362  * @param bufsize buffer size
    +
    1363  *
    +
    1364  * @return the number of bytes read from file descriptor if successful,
    +
    1365  * otherwise returns -1.
    +
    1366  *
    +
    1367  * @note
    +
    1368  * Be sure the return value does not mean the length of actual stored data.
    +
    1369  * It means how many bytes are read from the file descriptor, so the new-line
    +
    1370  * characters will be counted, but not stored.
    +
    1371  */
    +
    1372 static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize) {
    +
    1373 #ifdef ENABLE_OPENSSL
    +
    1374  if (client->ssl == NULL) {
    +
    1375  return qio_gets(client->socket, buf, bufsize, client->timeoutms);
    +
    1376  } else {
    +
    1377  if (bufsize <= 1) return -1;
    +
    1378 
    +
    1379  struct SslConn *ssl = client->ssl;
    +
    1380  ssize_t readcnt = 0;
    +
    1381  char *ptr;
    +
    1382 
    +
    1383  for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
    +
    1384  // wait readable
    +
    1385  //if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    +
    1386  // break;
    +
    1387  //}
    +
    1388 
    +
    1389  int rsize = SSL_read(ssl->ssl, ptr, 1);
    +
    1390  if (rsize != 1) {
    +
    1391  unsigned long sslerr = ERR_get_error();
    +
    1392  if (sslerr == SSL_ERROR_WANT_READ) {
    +
    1393  continue;
    +
    1394  }
    +
    1395 
    +
    1396  DEBUG("OpenSSL: %s (%d)",
    +
    1397  ERR_reason_error_string(sslerr), rsize);
    +
    1398  break;
    +
    1399  }
    +
    1400 
    +
    1401  readcnt++;
    +
    1402  if (*ptr == '\r') ptr--;
    +
    1403  else if (*ptr == '\n') break;
    +
    1404  }
    +
    1405 
    +
    1406  *ptr = '\0';
    +
    1407  DEBUG("SSL_read: %s (%zd)", buf, readcnt);
    +
    1408 
    +
    1409  if (readcnt > 0) return readcnt;
    +
    1410  return -1;
    +
    1411  }
    +
    1412 #else
    +
    1413  return qio_gets(client->socket, buf, bufsize, client->timeoutms);
    +
    1414 #endif
    +
    1415 }
    +
    1416 
    +
    1417 /**
    +
    1418  * qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
    +
    1419  *
    +
    1420  * @param client qhttpclient object pointer.
    +
    1421  * @param buf a buffer pointer for storing content. (can be NULL, then
    +
    1422  * read & throw out content)
    +
    1423  * @param length content size to read.
    +
    1424  *
    +
    1425  * @return number of bytes readed
    +
    1426  *
    +
    1427  * @code
    +
    1428  * off_t clength = 0;
    +
    1429  * int resno = client->readresponse(client, NULL, &clength);
    +
    1430  * if(clength > 0) {
    +
    1431  * void *buf = malloc(clength);
    +
    1432  * client->read(client, buf, clength);
    +
    1433  * }
    +
    1434  * @endcode
    +
    1435  */
    +
    1436 static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes) {
    +
    1437 #ifdef ENABLE_OPENSSL
    +
    1438  if (client->ssl == NULL) {
    +
    1439  return qio_read(client->socket, buf, nbytes, client->timeoutms);
    +
    1440  } else {
    +
    1441  if (nbytes == 0) return 0;
    +
    1442 
    +
    1443  struct SslConn *ssl = client->ssl;
    +
    1444  ssize_t total = 0;
    +
    1445  while (total < nbytes) {
    +
    1446  //if (qio_wait_readable(client->socket, client->timeoutms) <= 0) {
    +
    1447  // break;
    +
    1448  //}
    +
    1449 
    +
    1450  int rsize = 0;
    +
    1451  if (buf != NULL) {
    +
    1452  rsize = SSL_read(ssl->ssl, buf + total, nbytes - total);
    +
    1453  } else {
    +
    1454  char trash[1024];
    +
    1455  int toread = nbytes - total;
    +
    1456  if (toread > sizeof(trash)) toread = sizeof(trash);
    +
    1457  rsize = SSL_read(ssl->ssl, trash, toread);
    +
    1458  }
    +
    1459  if (rsize <= 0) {
    +
    1460  DEBUG("OpenSSL: %s (%d)",
    +
    1461  ERR_reason_error_string(ERR_get_error()), rsize);
    +
    1462  unsigned long sslerr = ERR_get_error();
    +
    1463  if (sslerr == SSL_ERROR_WANT_READ) {
    +
    1464  usleep(1);
    +
    1465  continue;
    +
    1466  }
    +
    1467  break;
    +
    1468  }
    +
    1469  total += rsize;
    +
    1470  }
    +
    1471 
    +
    1472  DEBUG("SSL_read: %zd", total);
    +
    1473  if (total > 0) return total;
    +
    1474  return -1;
    +
    1475  }
    +
    1476 #else
    +
    1477  return qio_read(client->socket, buf, nbytes, client->timeoutms);
    +
    1478 #endif
    +
    1479 }
    +
    1480 
    +
    1481 /**
    +
    1482  * qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
    +
    1483  *
    +
    1484  * @param client qhttpclient object pointer.
    +
    1485  * @param buf a data pointer.
    +
    1486  * @param length content size to write.
    +
    1487  *
    +
    1488  * @return number of bytes written.
    +
    1489  */
    +
    1490 static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes) {
    +
    1491 #ifdef ENABLE_OPENSSL
    +
    1492  if (client->ssl == NULL) {
    +
    1493  return qio_write(client->socket, buf, nbytes, -1);
    +
    1494  } else {
    +
    1495  if (nbytes == 0) return 0;
    +
    1496 
    +
    1497  struct SslConn *ssl = client->ssl;
    +
    1498  ssize_t total = 0;
    +
    1499  while (total < nbytes) {
    +
    1500  errno = 0;
    +
    1501  int wsize = SSL_write(ssl->ssl, buf + total, nbytes - total);
    +
    1502  if (wsize <= 0) {
    +
    1503  DEBUG("OpenSSL: %s (%d)",
    +
    1504  ERR_reason_error_string(ERR_get_error()), wsize);
    +
    1505  unsigned long sslerr = ERR_get_error();
    +
    1506  if (sslerr == SSL_ERROR_WANT_WRITE) {
    +
    1507  usleep(1);
    +
    1508  continue;
    +
    1509  }
    +
    1510  break;
    +
    1511  }
    +
    1512  total += wsize;
    +
    1513  }
    +
    1514 
    +
    1515  DEBUG("SSL_write: %zd/%zu", total, nbytes);
    +
    1516  if (total > 0) return total;
    +
    1517  return -1;
    +
    1518  }
    +
    1519 #else
    +
    1520  return qio_write(client->socket, buf, nbytes, -1);
    +
    1521 #endif
    +
    1522 }
    +
    1523 
    +
    1524 /**
    +
    1525  * qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save
    +
    1526  * into a file descriptor.
    +
    1527  *
    +
    1528  * @param client qhttpclient object pointer.
    +
    1529  * @param fd output file descriptor
    +
    1530  * @param nbytes the number of bytes to read and save.
    +
    1531  *
    +
    1532  * @return the number of bytes written if successful, otherwise returns -1.
    +
    1533  */
    +
    1534 static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes) {
    +
    1535  if (nbytes == 0)
    +
    1536  return 0;
    +
    1537 
    +
    1538  unsigned char buf[MAX_ATOMIC_DATA_SIZE];
    +
    1539 
    +
    1540  off_t total = 0; // total size sent
    +
    1541  while (total < nbytes) {
    +
    1542  size_t chunksize; // this time sending size
    +
    1543  if (nbytes - total <= sizeof(buf))
    +
    1544  chunksize = nbytes - total;
    +
    1545  else
    +
    1546  chunksize = sizeof(buf);
    +
    1547 
    +
    1548  // read
    +
    1549  ssize_t rsize = read_(client, buf, chunksize);
    +
    1550  if (rsize <= 0)
    +
    1551  break;
    +
    1552 
    +
    1553  // write
    +
    1554  ssize_t wsize = qio_write(fd, buf, rsize, -1);
    +
    1555  DEBUG("FILE write: %zd", wsize);
    +
    1556  if (wsize <= 0)
    +
    1557  break;
    +
    1558 
    +
    1559  total += wsize;
    +
    1560  if (rsize != wsize) {
    +
    1561  DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    +
    1562  break;
    +
    1563  }
    +
    1564  }
    +
    1565 
    +
    1566  if (total > 0)
    +
    1567  return total;
    +
    1568  return -1;
    +
    1569 }
    +
    1570 
    +
    1571 /**
    +
    1572  * qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
    +
    1573  *
    +
    1574  * @param client qhttpclient object pointer.
    +
    1575  * @param fd input file descriptor
    +
    1576  * @param nbytes the number of bytes to read and send.
    +
    1577  *
    +
    1578  * @return the number of bytes sent if successful, otherwise returns -1.
    +
    1579  */
    +
    1580 static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes) {
    +
    1581  if (nbytes == 0)
    +
    1582  return 0;
    +
    1583 
    +
    1584  unsigned char buf[MAX_ATOMIC_DATA_SIZE];
    +
    1585 
    +
    1586  off_t total = 0; // total size sent
    +
    1587  while (total < nbytes) {
    +
    1588  size_t chunksize; // this time sending size
    +
    1589  if (nbytes - total <= sizeof(buf))
    +
    1590  chunksize = nbytes - total;
    +
    1591  else
    +
    1592  chunksize = sizeof(buf);
    +
    1593 
    +
    1594  // read
    +
    1595  ssize_t rsize = qio_read(fd, buf, chunksize, -1);
    +
    1596  DEBUG("FILE read: %zd", rsize);
    +
    1597  if (rsize <= 0)
    +
    1598  break;
    +
    1599 
    +
    1600  // write
    +
    1601  ssize_t wsize = write_(client, buf, rsize);
    +
    1602  if (wsize <= 0)
    +
    1603  break;
    +
    1604 
    +
    1605  total += wsize;
    +
    1606  if (rsize != wsize) {
    +
    1607  DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    +
    1608  break;
    +
    1609  }
    +
    1610  }
    +
    1611 
    +
    1612  if (total > 0)
    +
    1613  return total;
    +
    1614  return -1;
    +
    1615 }
    +
    1616 
    +
    1617 /**
    +
    1618  * qhttpclient->close(): Closes the connection.
    +
    1619  *
    +
    1620  * @param qhttpclient_t HTTP object pointer
    +
    1621  *
    +
    1622  * @return true if successful, otherwise returns false
    +
    1623  *
    +
    1624  * @code
    +
    1625  * httpclient->close(httpclient);
    +
    1626  * @endcode
    +
    1627  */
    +
    1628 static bool _close(qhttpclient_t *client) {
    +
    1629  if (client->socket < 0)
    +
    1630  return false;
    +
    1631 
    +
    1632 #ifdef ENABLE_OPENSSL
    +
    1633  // release ssl connection
    +
    1634  if (client->ssl != NULL) {
    +
    1635  struct SslConn *ssl = client->ssl;
    +
    1636 
    +
    1637  if (ssl->ssl != NULL) {
    +
    1638  SSL_shutdown(ssl->ssl);
    +
    1639  SSL_free(ssl->ssl);
    +
    1640  ssl->ssl = NULL;
    +
    1641  }
    +
    1642 
    +
    1643  if (ssl->ctx != NULL) {
    +
    1644  SSL_CTX_free(ssl->ctx);
    +
    1645  ssl->ctx = NULL;
    +
    1646  }
    +
    1647  }
    +
    1648 #endif
    +
    1649 
    +
    1650  // shutdown connection
    +
    1651  if (client->ssl == NULL && MAX_SHUTDOWN_WAIT >= 0
    +
    1652  && shutdown(client->socket, SHUT_WR) == 0) {
    +
    1653  char buf[1024];
    +
    1654  while (qio_read(client->socket, buf, sizeof(buf), MAX_SHUTDOWN_WAIT) > 0);
    +
    1655  }
    +
    1656 
    +
    1657  // close connection
    +
    1658  close(client->socket);
    +
    1659  client->socket = -1;
    +
    1660  client->connclose = false;
    +
    1661 
    +
    1662  return true;
    +
    1663 }
    +
    1664 
    +
    1665 /**
    +
    1666  * qhttpclient->free(): Free object.
    +
    1667  *
    +
    1668  * @param qhttpclient_t HTTP object pointer
    +
    1669  *
    +
    1670  * @note
    +
    1671  * If the connection was not closed, it will close the connection first prior
    +
    1672  * to de-allocate object.
    +
    1673  *
    +
    1674  * @code
    +
    1675  * httpclient->free(httpclient);
    +
    1676  * @endcode
    +
    1677  */
    +
    1678 static void _free(qhttpclient_t *client) {
    +
    1679  if (client->socket >= 0) {
    +
    1680  client->close(client);
    +
    1681  }
    +
    1682 
    +
    1683  if (client->ssl != NULL)
    +
    1684  free(client->ssl);
    +
    1685  if (client->hostname != NULL)
    +
    1686  free(client->hostname);
    +
    1687  if (client->useragent != NULL)
    +
    1688  free(client->useragent);
    +
    1689 
    +
    1690  free(client);
    +
    1691 }
    +
    1692 
    +
    1693 #ifndef _DOXYGEN_SKIP
    +
    1694 static bool _set_socket_option(int socket) {
    +
    1695  bool ret = true;
    +
    1696 
    +
    1697  // linger option
    +
    1698  if (SET_TCP_LINGER_TIMEOUT > 0) {
    +
    1699  struct linger li;
    +
    1700  li.l_onoff = 1;
    +
    1701  li.l_linger = SET_TCP_LINGER_TIMEOUT;
    +
    1702  if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &li,
    +
    1703  sizeof(struct linger)) < 0) {
    +
    1704  ret = false;
    +
    1705  }
    +
    1706  }
    +
    1707 
    +
    1708  // nodelay option
    +
    1709  if (SET_TCP_NODELAY > 0) {
    +
    1710  int so_tcpnodelay = 1;
    +
    1711  if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &so_tcpnodelay,
    +
    1712  sizeof(so_tcpnodelay)) < 0) {
    +
    1713  ret = false;
    +
    1714  }
    +
    1715  }
    +
    1716 
    +
    1717  return ret;
    +
    1718 }
    +
    1719 
    +
    1720 static bool _parse_uri(const char *uri, bool *protocol, char *hostname,
    +
    1721  size_t namesize, int *port) {
    +
    1722 
    +
    1723  if (!strncasecmp(uri, "http://", CONST_STRLEN("http://"))) {
    +
    1724  *protocol = false;
    +
    1725  *port = 80;
    +
    1726  } else if (!strncasecmp(uri, "https://", CONST_STRLEN("https://"))) {
    +
    1727  *protocol = true;
    +
    1728  *port = 443;
    +
    1729  } else {
    +
    1730  return false;
    +
    1731  }
    +
    1732 
    +
    1733  char *t1 = strstr(uri, "://");
    +
    1734  t1 += 3;
    +
    1735  char *t2 = strstr(t1, "/");
    +
    1736  if (t2 == NULL)
    +
    1737  t2 = (char *) uri + strlen(uri);
    +
    1738 
    +
    1739  if (t2 - t1 + 1 > namesize)
    +
    1740  return false;
    +
    1741  qstrncpy(hostname, namesize, t1, t2 - t1);
    +
    1742 
    +
    1743  t1 = strstr(hostname, ":");
    +
    1744  if (t1 != NULL) {
    +
    1745  *t1 = '\0';
    +
    1746  *port = atoi(t1 + 1);
    +
    1747  }
    +
    1748 
    +
    1749  return true;
    +
    1750 }
    +
    1751 #endif /* _DOXYGEN_SKIP */
    +
    1752 
    +
    1753 #endif /* DISABLE_QHTTPCLIENT */
    +
    qgrow_t * qgrow(int options)
    Initialize grow.
    Definition: qgrow.c:134
    +
    static void settimeout(qhttpclient_t *client, int timeoutms)
    qhttpclient->settimeout(): Sets connection wait timeout.
    Definition: qhttpclient.c:363
    +
    static bool sendrequest(qhttpclient_t *client, const char *method, const char *uri, qlisttbl_t *reqheaders)
    qhttpclient->sendrequest(): Sends a HTTP request to the remote host.
    Definition: qhttpclient.c:1194
    +
    static off_t recvfile(qhttpclient_t *client, int fd, off_t nbytes)
    qhttpclient->recvfile(): Reads data from a HTTP/HTTPS stream and save into a file descriptor.
    Definition: qhttpclient.c:1534
    +
    static bool setssl(qhttpclient_t *client)
    qhttpclient->setssl(): Sets connection to HTTPS connection
    Definition: qhttpclient.c:323
    +
    static ssize_t read_(qhttpclient_t *client, void *buf, size_t nbytes)
    qhttpclient->read(): Reads data from a HTTP/HTTPS stream.
    Definition: qhttpclient.c:1436
    +
    static bool _close(qhttpclient_t *client)
    qhttpclient->close(): Closes the connection.
    Definition: qhttpclient.c:1628
    +
    static ssize_t write_(qhttpclient_t *client, const void *buf, size_t nbytes)
    qhttpclient->write(): Writes data to a HTTP/HTTPS stream.
    Definition: qhttpclient.c:1490
    +
    static void _free(qhttpclient_t *client)
    qhttpclient->free(): Free object.
    Definition: qhttpclient.c:1678
    +
    static void setkeepalive(qhttpclient_t *client, bool keepalive)
    qhttpclient->setkeepalive(): Sets KEEP-ALIVE feature on/off.
    Definition: qhttpclient.c:380
    +
    static int readresponse(qhttpclient_t *client, qlisttbl_t *resheaders, off_t *contentlength)
    qhttpclient->readresponse(): Reads HTTP response header from the remote host.
    Definition: qhttpclient.c:1293
    +
    static bool open_(qhttpclient_t *client)
    qhttpclient->open(): Opens a connection to the remote host.
    Definition: qhttpclient.c:417
    +
    static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->head(): Sends a HEAD request.
    Definition: qhttpclient.c:558
    +
    qhttpclient_t * qhttpclient(const char *destname, int port)
    Initialize & create new HTTP client.
    Definition: qhttpclient.c:245
    +
    static off_t sendfile_(qhttpclient_t *client, int fd, off_t nbytes)
    qhttpclient->sendfile(): Send file data to a HTTP/HTTPS stream.
    Definition: qhttpclient.c:1580
    +
    static ssize_t gets_(qhttpclient_t *client, char *buf, size_t bufsize)
    qhttpclient->gets(): Reads a text line from a HTTP/HTTPS stream.
    Definition: qhttpclient.c:1372
    +
    static bool put(qhttpclient_t *client, const char *uri, int fd, off_t length, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t sentbytes), void *userdata)
    qhttpclient->put(): Uploads a file to the remote host using PUT method.
    Definition: qhttpclient.c:926
    +
    static void * cmd(qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.
    Definition: qhttpclient.c:1099
    +
    static bool get(qhttpclient_t *client, const char *uri, int fd, off_t *savesize, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders, bool(*callback)(void *userdata, off_t recvbytes), void *userdata)
    qhttpclient->get(): Downloads a file from the remote host using GET method.
    Definition: qhttpclient.c:694
    +
    static void setuseragent(qhttpclient_t *client, const char *useragent)
    qhttpclient->setuseragent(): Sets user-agent string.
    Definition: qhttpclient.c:394
    +
    ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
    Write to a file descriptor.
    Definition: qio.c:159
    +
    int qio_wait_readable(int fd, int timeoutms)
    Test & wait until the file descriptor has readable data.
    Definition: qio.c:59
    +
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition: qio.c:118
    +
    ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
    Read a line from a file descriptor into the buffer pointed to until either a terminating newline or E...
    Definition: qio.c:257
    +
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition: qio.c:87
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150
    +
    bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
    Convert hostname to sockaddr_in structure.
    Definition: qsocket.c:135
    +
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition: qstring.c:325
    +
    char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
    Copy src string to dst no more than n bytes.
    Definition: qstring.c:344
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    diff --git a/doc/html/qio_8c.html b/doc/html/qio_8c.html index 4dc7ab8b..ea3483f5 100644 --- a/doc/html/qio_8c.html +++ b/doc/html/qio_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qio.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qio.c File Reference
    +
    +
    qio.c File Reference
    @@ -71,62 +71,46 @@

    Go to the source code of this file.

    - - +

    +

    Macros

    #define MAX_IOSEND_SIZE   (32 * 1024)
    +#define MAX_IOSEND_SIZE   (32 * 1024)
     
    - - + - + - + - + - + - + - + - +

    +

    Functions

    int qio_wait_readable (int fd, int timeoutms)
     Test & wait until the file descriptor has readable data.
     Test & wait until the file descriptor has readable data. More...
     
    int qio_wait_writable (int fd, int timeoutms)
     Test & wait until the file descriptor is ready for writing.
     Test & wait until the file descriptor is ready for writing. More...
     
    ssize_t qio_read (int fd, void *buf, size_t nbytes, int timeoutms)
     Read from a file descriptor.
     Read from a file descriptor. More...
     
    ssize_t qio_write (int fd, const void *buf, size_t nbytes, int timeoutms)
     Write to a file descriptor.
     Write to a file descriptor. More...
     
    off_t qio_send (int outfd, int infd, off_t nbytes, int timeoutms)
     Transfer data between file descriptors.
     Transfer data between file descriptors. More...
     
    ssize_t qio_gets (int fd, char *buf, size_t bufsize, int timeoutms)
     Read a line from a file descriptor into the buffer pointed to until either a terminating newline or EOF.
     Read a line from a file descriptor into the buffer pointed to until either a terminating newline or EOF. More...
     
    ssize_t qio_puts (int fd, const char *str, int timeoutms)
     Writes the string and a trailing newline to file descriptor.
     Writes the string and a trailing newline to file descriptor. More...
     
    ssize_t qio_printf (int fd, int timeoutms, const char *format,...)
     Formatted output to a file descriptor.
     Formatted output to a file descriptor. More...
     

    Detailed Description

    I/O handling APIs.

    Definition in file qio.c.

    -

    Macro Definition Documentation

    - -

    ◆ MAX_IOSEND_SIZE

    - -
    -
    - - - - -
    #define MAX_IOSEND_SIZE   (32 * 1024)
    -
    - -

    Definition at line 44 of file qio.c.

    - -
    -
    -

    Function Documentation

    - -

    ◆ qio_wait_readable()

    +

    Function Documentation

    + +

    ◆ qio_wait_readable()

    @@ -166,8 +150,8 @@

    -

    ◆ qio_wait_writable()

    + +

    ◆ qio_wait_writable()

    @@ -206,8 +190,8 @@

    -

    ◆ qio_read()

    + +

    ◆ qio_read()

    @@ -260,8 +244,8 @@

    -

    ◆ qio_write()

    + +

    ◆ qio_write()

    @@ -314,8 +298,8 @@

    -

    ◆ qio_send()

    + +

    ◆ qio_send()

    @@ -368,8 +352,8 @@

    -

    ◆ qio_gets()

    + +

    ◆ qio_gets()

    @@ -424,8 +408,8 @@

    -

    ◆ qio_puts()

    + +

    ◆ qio_puts()

    @@ -471,8 +455,8 @@

    -

    ◆ qio_printf()

    + +

    ◆ qio_printf()

    @@ -530,7 +514,7 @@

    diff --git a/doc/html/qio_8c.js b/doc/html/qio_8c.js index 0e26a048..017065d8 100644 --- a/doc/html/qio_8c.js +++ b/doc/html/qio_8c.js @@ -1,5 +1,6 @@ var qio_8c = [ + [ "MAX_IOSEND_SIZE", "qio_8c.html#a52246cf5f9d0bf7f359dfc0f92d555ed", null ], [ "qio_wait_readable", "qio_8c.html#a8a695a43ebb605d243e56ad48937862e", null ], [ "qio_wait_writable", "qio_8c.html#ac905811f0daa394ffef7beab68585a55", null ], [ "qio_read", "qio_8c.html#aa34911362f1793ed1e744d3eb63500db", null ], diff --git a/doc/html/qio_8c_source.html b/doc/html/qio_8c_source.html index b8d44b0d..58e112b0 100644 --- a/doc/html/qio_8c_source.html +++ b/doc/html/qio_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qio.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,365 +52,366 @@

    -
    qio.c
    +
    +
    qio.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qio.c I/O handling APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <stdarg.h>
    -
    37#include <string.h>
    -
    38#include <unistd.h>
    -
    39#include <poll.h>
    -
    40#include <errno.h>
    -
    41#include "qinternal.h"
    -
    42#include "utilities/qio.h"
    -
    43
    -
    44#define MAX_IOSEND_SIZE (32 * 1024)
    -
    45
    -
    46/**
    -
    47 * Test & wait until the file descriptor has readable data.
    -
    48 *
    -
    49 * @param fd file descriptor
    -
    50 * @param timeoutms wait timeout milliseconds. 0 for no wait,
    -
    51 * -1 for infinite wait
    -
    52 *
    -
    53 * @return 1 if readable, 0 on timeout, -1 if an error occurred.
    -
    54 *
    -
    55 * @note
    -
    56 * The argument timeoutms can be used to set maximum wait time for a socket
    -
    57 * descriptor.
    -
    58 */
    -
    59int qio_wait_readable(int fd, int timeoutms) {
    -
    60 struct pollfd fds[1];
    -
    61
    -
    62 fds[0].fd = fd;
    -
    63 fds[0].events = POLLIN;
    -
    64
    -
    65 int pollret = poll(fds, 1, timeoutms);
    -
    66 if (pollret == 0) {
    -
    67 errno = ETIMEDOUT;
    -
    68 return 0;
    -
    69 } else if (pollret < 0) {
    -
    70 return -1;
    -
    71 }
    -
    72
    -
    73 if (fds[0].revents & POLLIN)
    -
    74 return 1;
    -
    75 return -1;
    -
    76}
    -
    77
    -
    78/**
    -
    79 * Test & wait until the file descriptor is ready for writing.
    -
    80 *
    -
    81 * @param fd file descriptor
    -
    82 * @param timeoutms wait timeout milliseconds. 0 for no wait,
    -
    83 * -1 for infinite wait
    -
    84 *
    -
    85 * @return 1 if writable, 0 on timeout, -1 if an error occurred.
    -
    86 */
    -
    87int qio_wait_writable(int fd, int timeoutms) {
    -
    88 struct pollfd fds[1];
    -
    89
    -
    90 fds[0].fd = fd;
    -
    91 fds[0].events = POLLOUT;
    -
    92
    -
    93 int pollret = poll(fds, 1, timeoutms);
    -
    94 if (pollret == 0) {
    -
    95 errno = ETIMEDOUT;
    -
    96 return 0;
    -
    97 } else if (pollret < 0) {
    -
    98 return -1;
    -
    99 }
    -
    100
    -
    101 if (fds[0].revents & POLLOUT)
    -
    102 return 1;
    -
    103 return -1;
    -
    104}
    -
    105
    -
    106/**
    -
    107 * Read from a file descriptor.
    -
    108 *
    -
    109 * @param fd file descriptor
    -
    110 * @param buf data buffer pointer to write to
    -
    111 * @param nbytes the number of bytes to read from file descriptor & write
    -
    112 * into buffer
    -
    113 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    114 * wait
    -
    115 *
    -
    116 * @return the number of bytes read if successful, 0 on timeout, -1 for error.
    -
    117 */
    -
    118ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms) {
    -
    119 if (nbytes == 0)
    -
    120 return 0;
    -
    121
    -
    122 ssize_t total = 0;
    -
    123 while (total < nbytes) {
    -
    124 if (timeoutms >= 0 && qio_wait_readable(fd, timeoutms) <= 0)
    -
    125 break;
    -
    126
    -
    127 ssize_t rsize = read(fd, buf + total, nbytes - total);
    -
    128 if (rsize <= 0) {
    -
    129 if (errno == EAGAIN || errno == EINPROGRESS) {
    -
    130 // possible with non-block io
    -
    131 usleep(1);
    -
    132 continue;
    -
    133 }
    -
    134 break;
    -
    135 }
    -
    136 total += rsize;
    -
    137 }
    -
    138
    -
    139 if (total > 0)
    -
    140 return total;
    -
    141 else if (errno == ETIMEDOUT)
    -
    142 return 0;
    -
    143 return -1;
    -
    144}
    -
    145
    -
    146/**
    -
    147 * Write to a file descriptor.
    -
    148 *
    -
    149 * @param fd file descriptor
    -
    150 * @param buf data buffer pointer to read from
    -
    151 * @param nbytes the number of bytes to write to file descriptor & read
    -
    152 * from buffer
    -
    153 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    154 * wait
    -
    155 *
    -
    156 * @return the number of bytes written if successful, 0 on timeout,
    -
    157 * -1 for error.
    -
    158 */
    -
    159ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms) {
    -
    160 if (nbytes == 0)
    -
    161 return 0;
    -
    162
    -
    163 ssize_t total = 0;
    -
    164 while (total < nbytes) {
    -
    165 if (timeoutms >= 0 && qio_wait_writable(fd, timeoutms) <= 0)
    -
    166 break;
    -
    167 ssize_t wsize = write(fd, buf + total, nbytes - total);
    -
    168 if (wsize <= 0) {
    -
    169 if (errno == EAGAIN || errno == EINPROGRESS) {
    -
    170 // possible with non-block io
    -
    171 usleep(1);
    -
    172 continue;
    -
    173 }
    -
    174 break;
    -
    175 }
    -
    176 total += wsize;
    -
    177 }
    -
    178
    -
    179 if (total > 0)
    -
    180 return total;
    -
    181 else if (errno == ETIMEDOUT)
    -
    182 return 0;
    -
    183 return -1;
    -
    184}
    -
    185
    -
    186/**
    -
    187 * Transfer data between file descriptors
    -
    188 *
    -
    189 * @param outfd output file descriptor
    -
    190 * @param infd input file descriptor
    -
    191 * @param nbytes the number of bytes to copy between file descriptors.
    -
    192 * 0 means transfer until end of infd.
    -
    193 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    194 * wait
    -
    195 *
    -
    196 * @return the number of bytes transferred if successful, 0 on timeout,
    -
    197 * -1 for error.
    -
    198 */
    -
    199off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms) {
    -
    200 if (nbytes == 0)
    -
    201 return 0;
    -
    202
    -
    203 unsigned char buf[MAX_IOSEND_SIZE];
    -
    204
    -
    205 off_t total = 0; // total size sent
    -
    206 while (total < nbytes) {
    -
    207 size_t chunksize; // this time sending size
    -
    208 if (nbytes - total <= sizeof(buf))
    -
    209 chunksize = nbytes - total;
    -
    210 else
    -
    211 chunksize = sizeof(buf);
    -
    212
    -
    213 // read
    -
    214 ssize_t rsize = qio_read(infd, buf, chunksize, timeoutms);
    -
    215 DEBUG("read %zd", rsize);
    -
    216 if (rsize <= 0)
    -
    217 break;
    -
    218
    -
    219 // write
    -
    220 ssize_t wsize = qio_write(outfd, buf, rsize, timeoutms);
    -
    221 DEBUG("write %zd", wsize);
    -
    222 if (wsize <= 0)
    -
    223 break;
    -
    224
    -
    225 total += wsize;
    -
    226 if (rsize != wsize) {
    -
    227 DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    -
    228 break;
    -
    229 }
    -
    230 }
    -
    231
    -
    232 if (total > 0)
    -
    233 return total;
    -
    234 else if (errno == ETIMEDOUT)
    -
    235 return 0;
    -
    236 return -1;
    -
    237}
    -
    238
    -
    239/**
    -
    240 * Read a line from a file descriptor into the buffer pointed to until either a
    -
    241 * terminating newline or EOF. New-line characters(CR, LF ) will not be stored
    -
    242 * into buffer.
    -
    243 *
    -
    244 * @param fd file descriptor
    -
    245 * @param buf data buffer pointer
    -
    246 * @param bufsize buffer size
    -
    247 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    248 * wait
    -
    249 *
    -
    250 * @return the number of bytes read if successful, 0 on timeout, -1 for error.
    -
    251 *
    -
    252 * @note
    -
    253 * Be sure the return value does not mean the length of actual stored data.
    -
    254 * It means how many bytes are readed from the file descriptor,
    -
    255 * so the new-line characters will be counted, but not be stored.
    -
    256 */
    -
    257ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms) {
    -
    258 if (bufsize <= 1)
    -
    259 return -1;
    -
    260
    -
    261 ssize_t readcnt = 0;
    -
    262 char *ptr;
    -
    263 for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
    -
    264 ssize_t rsize = qio_read(fd, ptr, 1, timeoutms);
    -
    265 if (rsize != 1) {
    -
    266 if (errno == EAGAIN || errno == EINPROGRESS) {
    -
    267 // possible with non-block io
    -
    268 usleep(1);
    -
    269 continue;
    -
    270 }
    -
    271 break;
    -
    272 }
    -
    273
    -
    274 readcnt++;
    -
    275 if (*ptr == '\r')
    -
    276 ptr--;
    -
    277 else if (*ptr == '\n')
    -
    278 break;
    -
    279 }
    -
    280
    -
    281 *ptr = '\0';
    -
    282
    -
    283 if (readcnt > 0)
    -
    284 return readcnt;
    -
    285 else if (errno == ETIMEDOUT)
    -
    286 return 0;
    -
    287 return -1;
    -
    288}
    -
    289
    -
    290/**
    -
    291 * Writes the string and a trailing newline to file descriptor.
    -
    292 *
    -
    293 * @param fd file descriptor
    -
    294 * @param str string pointer
    -
    295 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    296 * wait
    -
    297 *
    -
    298 * @return the number of bytes written including trailing newline characters
    -
    299 * if successful, 0 for timeout and -1 for errors.
    -
    300 */
    -
    301ssize_t qio_puts(int fd, const char *str, int timeoutms) {
    -
    302 size_t strsize = strlen(str);
    -
    303 char *newstr = (char *) malloc(strsize + 1 + 1);
    -
    304 if (newstr == NULL)
    -
    305 return -1;
    -
    306 strncpy(newstr, str, strsize);
    -
    307 newstr[strsize] = '\n';
    -
    308 newstr[strsize + 1] = '\0';
    -
    309 ssize_t ret = qio_write(fd, newstr, strsize + 1, timeoutms);
    -
    310 free(newstr);
    -
    311 return ret;
    -
    312}
    -
    313
    -
    314/**
    -
    315 * Formatted output to a file descriptor
    -
    316 *
    -
    317 * @param fd file descriptor
    -
    318 * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    -
    319 * wait
    -
    320 * @param format format string
    -
    321 *
    -
    322 * @return the number of bytes written including trailing newline characters
    -
    323 * if successful, 0 for timeout and -1 for errors.
    -
    324 */
    -
    325ssize_t qio_printf(int fd, int timeoutms, const char *format, ...) {
    -
    326 char *buf;
    -
    327 DYNAMIC_VSPRINTF(buf, format);
    -
    328 if (buf == NULL)
    -
    329 return -1;
    -
    330
    -
    331 ssize_t ret = qio_write(fd, buf, strlen(buf), timeoutms);
    -
    332 free(buf);
    -
    333
    -
    334 return ret;
    -
    335}
    -
    ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
    Write to a file descriptor.
    Definition qio.c:159
    -
    ssize_t qio_puts(int fd, const char *str, int timeoutms)
    Writes the string and a trailing newline to file descriptor.
    Definition qio.c:301
    -
    int qio_wait_readable(int fd, int timeoutms)
    Test & wait until the file descriptor has readable data.
    Definition qio.c:59
    -
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition qio.c:118
    -
    off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms)
    Transfer data between file descriptors.
    Definition qio.c:199
    -
    ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
    Read a line from a file descriptor into the buffer pointed to until either a terminating newline or E...
    Definition qio.c:257
    -
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition qio.c:87
    -
    ssize_t qio_printf(int fd, int timeoutms, const char *format,...)
    Formatted output to a file descriptor.
    Definition qio.c:325
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qio.c I/O handling APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <stdarg.h>
    +
    37 #include <string.h>
    +
    38 #include <unistd.h>
    +
    39 #include <poll.h>
    +
    40 #include <errno.h>
    +
    41 #include "qinternal.h"
    +
    42 #include "utilities/qio.h"
    +
    43 
    +
    44 #define MAX_IOSEND_SIZE (32 * 1024)
    +
    45 
    +
    46 /**
    +
    47  * Test & wait until the file descriptor has readable data.
    +
    48  *
    +
    49  * @param fd file descriptor
    +
    50  * @param timeoutms wait timeout milliseconds. 0 for no wait,
    +
    51  * -1 for infinite wait
    +
    52  *
    +
    53  * @return 1 if readable, 0 on timeout, -1 if an error occurred.
    +
    54  *
    +
    55  * @note
    +
    56  * The argument timeoutms can be used to set maximum wait time for a socket
    +
    57  * descriptor.
    +
    58  */
    +
    59 int qio_wait_readable(int fd, int timeoutms) {
    +
    60  struct pollfd fds[1];
    +
    61 
    +
    62  fds[0].fd = fd;
    +
    63  fds[0].events = POLLIN;
    +
    64 
    +
    65  int pollret = poll(fds, 1, timeoutms);
    +
    66  if (pollret == 0) {
    +
    67  errno = ETIMEDOUT;
    +
    68  return 0;
    +
    69  } else if (pollret < 0) {
    +
    70  return -1;
    +
    71  }
    +
    72 
    +
    73  if (fds[0].revents & POLLIN)
    +
    74  return 1;
    +
    75  return -1;
    +
    76 }
    +
    77 
    +
    78 /**
    +
    79  * Test & wait until the file descriptor is ready for writing.
    +
    80  *
    +
    81  * @param fd file descriptor
    +
    82  * @param timeoutms wait timeout milliseconds. 0 for no wait,
    +
    83  * -1 for infinite wait
    +
    84  *
    +
    85  * @return 1 if writable, 0 on timeout, -1 if an error occurred.
    +
    86  */
    +
    87 int qio_wait_writable(int fd, int timeoutms) {
    +
    88  struct pollfd fds[1];
    +
    89 
    +
    90  fds[0].fd = fd;
    +
    91  fds[0].events = POLLOUT;
    +
    92 
    +
    93  int pollret = poll(fds, 1, timeoutms);
    +
    94  if (pollret == 0) {
    +
    95  errno = ETIMEDOUT;
    +
    96  return 0;
    +
    97  } else if (pollret < 0) {
    +
    98  return -1;
    +
    99  }
    +
    100 
    +
    101  if (fds[0].revents & POLLOUT)
    +
    102  return 1;
    +
    103  return -1;
    +
    104 }
    +
    105 
    +
    106 /**
    +
    107  * Read from a file descriptor.
    +
    108  *
    +
    109  * @param fd file descriptor
    +
    110  * @param buf data buffer pointer to write to
    +
    111  * @param nbytes the number of bytes to read from file descriptor & write
    +
    112  * into buffer
    +
    113  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    114  * wait
    +
    115  *
    +
    116  * @return the number of bytes read if successful, 0 on timeout, -1 for error.
    +
    117  */
    +
    118 ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms) {
    +
    119  if (nbytes == 0)
    +
    120  return 0;
    +
    121 
    +
    122  ssize_t total = 0;
    +
    123  while (total < nbytes) {
    +
    124  if (timeoutms >= 0 && qio_wait_readable(fd, timeoutms) <= 0)
    +
    125  break;
    +
    126 
    +
    127  ssize_t rsize = read(fd, buf + total, nbytes - total);
    +
    128  if (rsize <= 0) {
    +
    129  if (errno == EAGAIN || errno == EINPROGRESS) {
    +
    130  // possible with non-block io
    +
    131  usleep(1);
    +
    132  continue;
    +
    133  }
    +
    134  break;
    +
    135  }
    +
    136  total += rsize;
    +
    137  }
    +
    138 
    +
    139  if (total > 0)
    +
    140  return total;
    +
    141  else if (errno == ETIMEDOUT)
    +
    142  return 0;
    +
    143  return -1;
    +
    144 }
    +
    145 
    +
    146 /**
    +
    147  * Write to a file descriptor.
    +
    148  *
    +
    149  * @param fd file descriptor
    +
    150  * @param buf data buffer pointer to read from
    +
    151  * @param nbytes the number of bytes to write to file descriptor & read
    +
    152  * from buffer
    +
    153  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    154  * wait
    +
    155  *
    +
    156  * @return the number of bytes written if successful, 0 on timeout,
    +
    157  * -1 for error.
    +
    158  */
    +
    159 ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms) {
    +
    160  if (nbytes == 0)
    +
    161  return 0;
    +
    162 
    +
    163  ssize_t total = 0;
    +
    164  while (total < nbytes) {
    +
    165  if (timeoutms >= 0 && qio_wait_writable(fd, timeoutms) <= 0)
    +
    166  break;
    +
    167  ssize_t wsize = write(fd, buf + total, nbytes - total);
    +
    168  if (wsize <= 0) {
    +
    169  if (errno == EAGAIN || errno == EINPROGRESS) {
    +
    170  // possible with non-block io
    +
    171  usleep(1);
    +
    172  continue;
    +
    173  }
    +
    174  break;
    +
    175  }
    +
    176  total += wsize;
    +
    177  }
    +
    178 
    +
    179  if (total > 0)
    +
    180  return total;
    +
    181  else if (errno == ETIMEDOUT)
    +
    182  return 0;
    +
    183  return -1;
    +
    184 }
    +
    185 
    +
    186 /**
    +
    187  * Transfer data between file descriptors
    +
    188  *
    +
    189  * @param outfd output file descriptor
    +
    190  * @param infd input file descriptor
    +
    191  * @param nbytes the number of bytes to copy between file descriptors.
    +
    192  * 0 means transfer until end of infd.
    +
    193  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    194  * wait
    +
    195  *
    +
    196  * @return the number of bytes transferred if successful, 0 on timeout,
    +
    197  * -1 for error.
    +
    198  */
    +
    199 off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms) {
    +
    200  if (nbytes == 0)
    +
    201  return 0;
    +
    202 
    +
    203  unsigned char buf[MAX_IOSEND_SIZE];
    +
    204 
    +
    205  off_t total = 0; // total size sent
    +
    206  while (total < nbytes) {
    +
    207  size_t chunksize; // this time sending size
    +
    208  if (nbytes - total <= sizeof(buf))
    +
    209  chunksize = nbytes - total;
    +
    210  else
    +
    211  chunksize = sizeof(buf);
    +
    212 
    +
    213  // read
    +
    214  ssize_t rsize = qio_read(infd, buf, chunksize, timeoutms);
    +
    215  DEBUG("read %zd", rsize);
    +
    216  if (rsize <= 0)
    +
    217  break;
    +
    218 
    +
    219  // write
    +
    220  ssize_t wsize = qio_write(outfd, buf, rsize, timeoutms);
    +
    221  DEBUG("write %zd", wsize);
    +
    222  if (wsize <= 0)
    +
    223  break;
    +
    224 
    +
    225  total += wsize;
    +
    226  if (rsize != wsize) {
    +
    227  DEBUG("size mismatch. read:%zd, write:%zd", rsize, wsize);
    +
    228  break;
    +
    229  }
    +
    230  }
    +
    231 
    +
    232  if (total > 0)
    +
    233  return total;
    +
    234  else if (errno == ETIMEDOUT)
    +
    235  return 0;
    +
    236  return -1;
    +
    237 }
    +
    238 
    +
    239 /**
    +
    240  * Read a line from a file descriptor into the buffer pointed to until either a
    +
    241  * terminating newline or EOF. New-line characters(CR, LF ) will not be stored
    +
    242  * into buffer.
    +
    243  *
    +
    244  * @param fd file descriptor
    +
    245  * @param buf data buffer pointer
    +
    246  * @param bufsize buffer size
    +
    247  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    248  * wait
    +
    249  *
    +
    250  * @return the number of bytes read if successful, 0 on timeout, -1 for error.
    +
    251  *
    +
    252  * @note
    +
    253  * Be sure the return value does not mean the length of actual stored data.
    +
    254  * It means how many bytes are readed from the file descriptor,
    +
    255  * so the new-line characters will be counted, but not be stored.
    +
    256  */
    +
    257 ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms) {
    +
    258  if (bufsize <= 1)
    +
    259  return -1;
    +
    260 
    +
    261  ssize_t readcnt = 0;
    +
    262  char *ptr;
    +
    263  for (ptr = buf; readcnt < (bufsize - 1); ptr++) {
    +
    264  ssize_t rsize = qio_read(fd, ptr, 1, timeoutms);
    +
    265  if (rsize != 1) {
    +
    266  if (errno == EAGAIN || errno == EINPROGRESS) {
    +
    267  // possible with non-block io
    +
    268  usleep(1);
    +
    269  continue;
    +
    270  }
    +
    271  break;
    +
    272  }
    +
    273 
    +
    274  readcnt++;
    +
    275  if (*ptr == '\r')
    +
    276  ptr--;
    +
    277  else if (*ptr == '\n')
    +
    278  break;
    +
    279  }
    +
    280 
    +
    281  *ptr = '\0';
    +
    282 
    +
    283  if (readcnt > 0)
    +
    284  return readcnt;
    +
    285  else if (errno == ETIMEDOUT)
    +
    286  return 0;
    +
    287  return -1;
    +
    288 }
    +
    289 
    +
    290 /**
    +
    291  * Writes the string and a trailing newline to file descriptor.
    +
    292  *
    +
    293  * @param fd file descriptor
    +
    294  * @param str string pointer
    +
    295  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    296  * wait
    +
    297  *
    +
    298  * @return the number of bytes written including trailing newline characters
    +
    299  * if successful, 0 for timeout and -1 for errors.
    +
    300  */
    +
    301 ssize_t qio_puts(int fd, const char *str, int timeoutms) {
    +
    302  size_t strsize = strlen(str);
    +
    303  char *newstr = (char *) malloc(strsize + 1 + 1);
    +
    304  if (newstr == NULL)
    +
    305  return -1;
    +
    306  strncpy(newstr, str, strsize);
    +
    307  newstr[strsize] = '\n';
    +
    308  newstr[strsize + 1] = '\0';
    +
    309  ssize_t ret = qio_write(fd, newstr, strsize + 1, timeoutms);
    +
    310  free(newstr);
    +
    311  return ret;
    +
    312 }
    +
    313 
    +
    314 /**
    +
    315  * Formatted output to a file descriptor
    +
    316  *
    +
    317  * @param fd file descriptor
    +
    318  * @param timeoutms wait timeout milliseconds. 0 for no wait, -1 for infinite
    +
    319  * wait
    +
    320  * @param format format string
    +
    321  *
    +
    322  * @return the number of bytes written including trailing newline characters
    +
    323  * if successful, 0 for timeout and -1 for errors.
    +
    324  */
    +
    325 ssize_t qio_printf(int fd, int timeoutms, const char *format, ...) {
    +
    326  char *buf;
    +
    327  DYNAMIC_VSPRINTF(buf, format);
    +
    328  if (buf == NULL)
    +
    329  return -1;
    +
    330 
    +
    331  ssize_t ret = qio_write(fd, buf, strlen(buf), timeoutms);
    +
    332  free(buf);
    +
    333 
    +
    334  return ret;
    +
    335 }
    +
    ssize_t qio_write(int fd, const void *buf, size_t nbytes, int timeoutms)
    Write to a file descriptor.
    Definition: qio.c:159
    +
    ssize_t qio_puts(int fd, const char *str, int timeoutms)
    Writes the string and a trailing newline to file descriptor.
    Definition: qio.c:301
    +
    int qio_wait_readable(int fd, int timeoutms)
    Test & wait until the file descriptor has readable data.
    Definition: qio.c:59
    +
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition: qio.c:118
    +
    off_t qio_send(int outfd, int infd, off_t nbytes, int timeoutms)
    Transfer data between file descriptors.
    Definition: qio.c:199
    +
    ssize_t qio_gets(int fd, char *buf, size_t bufsize, int timeoutms)
    Read a line from a file descriptor into the buffer pointed to until either a terminating newline or E...
    Definition: qio.c:257
    +
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition: qio.c:87
    +
    ssize_t qio_printf(int fd, int timeoutms, const char *format,...)
    Formatted output to a file descriptor.
    Definition: qio.c:325
    diff --git a/doc/html/qlibc_8h.html b/doc/html/qlibc_8h.html index e5d1476e..97c129de 100644 --- a/doc/html/qlibc_8h.html +++ b/doc/html/qlibc_8h.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include/qlibc/qlibc.h File Reference +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include/qlibc/qlibc.h File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,13 +52,14 @@
    -
    qlibc.h File Reference
    +
    +
    qlibc.h File Reference
    @@ -77,7 +77,7 @@ diff --git a/doc/html/qlibc_8h_source.html b/doc/html/qlibc_8h_source.html index b6daaf1e..49369c98 100644 --- a/doc/html/qlibc_8h_source.html +++ b/doc/html/qlibc_8h_source.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include/qlibc/qlibc.h Source File +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include/qlibc/qlibc.h Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,87 +52,88 @@
    -
    qlibc.h
    +
    +
    qlibc.h
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * qlibc header file.
    -
    31 *
    -
    32 * @file qlibc.h
    -
    33 */
    -
    34
    -
    35#ifndef QLIBC_H
    -
    36#define QLIBC_H
    -
    37
    -
    38/* containers */
    -
    39#include "containers/qtreetbl.h"
    -
    40#include "containers/qhashtbl.h"
    -
    41#include "containers/qhasharr.h"
    -
    42#include "containers/qlisttbl.h"
    -
    43#include "containers/qlist.h"
    -
    44#include "containers/qvector.h"
    -
    45#include "containers/qqueue.h"
    -
    46#include "containers/qstack.h"
    -
    47#include "containers/qgrow.h"
    -
    48
    -
    49/* utilities */
    -
    50#include "utilities/qcount.h"
    -
    51#include "utilities/qencode.h"
    -
    52#include "utilities/qfile.h"
    -
    53#include "utilities/qhash.h"
    -
    54#include "utilities/qio.h"
    -
    55#include "utilities/qsocket.h"
    -
    56#include "utilities/qstring.h"
    -
    57#include "utilities/qsystem.h"
    -
    58#include "utilities/qtime.h"
    -
    59
    -
    60/* ipc */
    -
    61#include "ipc/qsem.h"
    -
    62#include "ipc/qshm.h"
    -
    63
    -
    64#endif /* QLIBC_H */
    -
    65
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * qlibc header file.
    +
    31  *
    +
    32  * @file qlibc.h
    +
    33  */
    +
    34 
    +
    35 #ifndef QLIBC_H
    +
    36 #define QLIBC_H
    +
    37 
    +
    38 /* containers */
    +
    39 #include "containers/qtreetbl.h"
    +
    40 #include "containers/qhashtbl.h"
    +
    41 #include "containers/qhasharr.h"
    +
    42 #include "containers/qlisttbl.h"
    +
    43 #include "containers/qlist.h"
    +
    44 #include "containers/qvector.h"
    +
    45 #include "containers/qqueue.h"
    +
    46 #include "containers/qstack.h"
    +
    47 #include "containers/qgrow.h"
    +
    48 
    +
    49 /* utilities */
    +
    50 #include "utilities/qcount.h"
    +
    51 #include "utilities/qencode.h"
    +
    52 #include "utilities/qfile.h"
    +
    53 #include "utilities/qhash.h"
    +
    54 #include "utilities/qio.h"
    +
    55 #include "utilities/qsocket.h"
    +
    56 #include "utilities/qstring.h"
    +
    57 #include "utilities/qsystem.h"
    +
    58 #include "utilities/qtime.h"
    +
    59 
    +
    60 /* ipc */
    +
    61 #include "ipc/qsem.h"
    +
    62 #include "ipc/qshm.h"
    +
    63 
    +
    64 #endif /* QLIBC_H */
    +
    65 
    diff --git a/doc/html/qlibcext_8h.html b/doc/html/qlibcext_8h.html index db43cdc5..8d894f89 100644 --- a/doc/html/qlibcext_8h.html +++ b/doc/html/qlibcext_8h.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include/qlibc/qlibcext.h File Reference +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include/qlibc/qlibcext.h File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,13 +52,14 @@

    -
    qlibcext.h File Reference
    +
    +
    qlibcext.h File Reference
    @@ -77,7 +77,7 @@ diff --git a/doc/html/qlibcext_8h_source.html b/doc/html/qlibcext_8h_source.html index b5dbc8d2..bf05cf21 100644 --- a/doc/html/qlibcext_8h_source.html +++ b/doc/html/qlibcext_8h_source.html @@ -1,11 +1,11 @@ - + - - + + -qLibc: /Users/wolkykim/github/qlibc/include/qlibc/qlibcext.h Source File +qLibc: /mnt/dev-data.vhd/MyCode/OpenSourceCode/qlibc-dev/tmp/qlibc.SunBeau.git.pgsql.2/include/qlibc/qlibcext.h Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,67 +52,68 @@
    -
    qlibcext.h
    +
    +
    qlibcext.h
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * qlibc extension header file.
    -
    31 *
    -
    32 * @file qlibcext.h
    -
    33 */
    -
    34
    -
    35#ifndef QLIBCEXT_H
    -
    36#define QLIBCEXT_H
    -
    37
    -
    38#include "extensions/qconfig.h"
    -
    39#include "extensions/qaconf.h"
    -
    40#include "extensions/qlog.h"
    -
    41#include "extensions/qhttpclient.h"
    -
    42#include "extensions/qdatabase.h"
    -
    43#include "extensions/qtokenbucket.h"
    -
    44
    -
    45#endif /* QLIBCEXT_H */
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * qlibc extension header file.
    +
    31  *
    +
    32  * @file qlibcext.h
    +
    33  */
    +
    34 
    +
    35 #ifndef QLIBCEXT_H
    +
    36 #define QLIBCEXT_H
    +
    37 
    +
    38 #include "extensions/qconfig.h"
    +
    39 #include "extensions/qaconf.h"
    +
    40 #include "extensions/qlog.h"
    +
    41 #include "extensions/qhttpclient.h"
    +
    42 #include "extensions/qdatabase.h"
    +
    43 #include "extensions/qtokenbucket.h"
    +
    44 
    +
    45 #endif /* QLIBCEXT_H */
    diff --git a/doc/html/qlist_8c.html b/doc/html/qlist_8c.html index 9acb0c07..55f2ad3d 100644 --- a/doc/html/qlist_8c.html +++ b/doc/html/qlist_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qlist.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qlist.c File Reference
    +
    +
    qlist.c File Reference
    @@ -70,82 +70,82 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - - - - - - + + + + + + - + - + - + - +

    +

    Functions

    qlist_t * qlist (int options)
     Create new qlist_t linked-list container.
     
    qlist_t * qlist (int options)
     Create new qlist_t linked-list container. More...
     
    size_t qlist_setsize (qlist_t *list, size_t max)
     qlist->setsize(): Limit maximum number of elements allowed in this list.
     qlist->setsize(): Limit maximum number of elements allowed in this list. More...
     
    bool qlist_addfirst (qlist_t *list, const void *data, size_t size)
     qlist->addfirst(): Inserts a element at the beginning of this list.
     qlist->addfirst(): Inserts a element at the beginning of this list. More...
     
    bool qlist_addlast (qlist_t *list, const void *data, size_t size)
     qlist->addlast(): Appends a element to the end of this list.
     qlist->addlast(): Appends a element to the end of this list. More...
     
    bool qlist_addat (qlist_t *list, int index, const void *data, size_t size)
     qlist->addat(): Inserts a element at the specified position in this list.
     qlist->addat(): Inserts a element at the specified position in this list. More...
     
    void * qlist_getfirst (qlist_t *list, size_t *size, bool newmem)
     qlist->getfirst(): Returns the first element in this list.
     
    void * qlist_getlast (qlist_t *list, size_t *size, bool newmem)
     qlist->getlast(): Returns the last element in this list.
     
    void * qlist_getat (qlist_t *list, int index, size_t *size, bool newmem)
     qlist->getat(): Returns the element at the specified position in this list.
     
    void * qlist_popfirst (qlist_t *list, size_t *size)
     qlist->popfirst(): Returns and remove the first element in this list.
     
    void * qlist_poplast (qlist_t *list, size_t *size)
     qlist->getlast(): Returns and remove the last element in this list.
     
    void * qlist_popat (qlist_t *list, int index, size_t *size)
     qlist->popat(): Returns and remove the element at the specified position in this list.
     
    void * qlist_getfirst (qlist_t *list, size_t *size, bool newmem)
     qlist->getfirst(): Returns the first element in this list. More...
     
    void * qlist_getlast (qlist_t *list, size_t *size, bool newmem)
     qlist->getlast(): Returns the last element in this list. More...
     
    void * qlist_getat (qlist_t *list, int index, size_t *size, bool newmem)
     qlist->getat(): Returns the element at the specified position in this list. More...
     
    void * qlist_popfirst (qlist_t *list, size_t *size)
     qlist->popfirst(): Returns and remove the first element in this list. More...
     
    void * qlist_poplast (qlist_t *list, size_t *size)
     qlist->getlast(): Returns and remove the last element in this list. More...
     
    void * qlist_popat (qlist_t *list, int index, size_t *size)
     qlist->popat(): Returns and remove the element at the specified position in this list. More...
     
    bool qlist_removefirst (qlist_t *list)
     qlist->removefirst(): Removes the first element in this list.
     qlist->removefirst(): Removes the first element in this list. More...
     
    bool qlist_removelast (qlist_t *list)
     qlist->removelast(): Removes the last element in this list.
     qlist->removelast(): Removes the last element in this list. More...
     
    bool qlist_removeat (qlist_t *list, int index)
     qlist->removeat(): Removes the element at the specified position in this list.
     qlist->removeat(): Removes the element at the specified position in this list. More...
     
    bool qlist_getnext (qlist_t *list, qlist_obj_t *obj, bool newmem)
     qlist->getnext(): Get next element in this list.
     qlist->getnext(): Get next element in this list. More...
     
    size_t qlist_size (qlist_t *list)
     qlist->size(): Returns the number of elements in this list.
     qlist->size(): Returns the number of elements in this list. More...
     
    size_t qlist_datasize (qlist_t *list)
     qlist->size(): Returns the sum of total element size.
     qlist->size(): Returns the sum of total element size. More...
     
    void qlist_reverse (qlist_t *list)
     qlist->reverse(): Reverse the order of elements.
     qlist->reverse(): Reverse the order of elements. More...
     
    void qlist_clear (qlist_t *list)
     qlist->clear(): Removes all of the elements from this list.
     qlist->clear(): Removes all of the elements from this list. More...
     
    void * qlist_toarray (qlist_t *list, size_t *size)
     qlist->toarray(): Returns the serialized chunk containing all the elements in this list.
     
    char * qlist_tostring (qlist_t *list)
     qlist->tostring(): Returns a string representation of this list, containing string representation of each element.
     
    void * qlist_toarray (qlist_t *list, size_t *size)
     qlist->toarray(): Returns the serialized chunk containing all the elements in this list. More...
     
    char * qlist_tostring (qlist_t *list)
     qlist->tostring(): Returns a string representation of this list, containing string representation of each element. More...
     
    bool qlist_debug (qlist_t *list, FILE *out)
     qlist->debug(): Prints out stored elements for debugging purpose.
     qlist->debug(): Prints out stored elements for debugging purpose. More...
     
    void qlist_lock (qlist_t *list)
     qlist->lock(): Enters critical section.
     qlist->lock(): Enters critical section. More...
     
    void qlist_unlock (qlist_t *list)
     qlist->unlock(): Leaves critical section.
     qlist->unlock(): Leaves critical section. More...
     
    void qlist_free (qlist_t *list)
     qlist->free(): Free qlist_t.
     qlist->free(): Free qlist_t. More...
     

    Detailed Description

    @@ -166,7 +166,7 @@
    | DATA B |
    +----------------------------------------+
    // create a list.
    -
    qlist_t *list = qlist(QLIST_THREADSAFE);
    +
    qlist_t *list = qlist(QLIST_THREADSAFE);
    // insert elements
    list->addlast(list, "e1", sizeof("e1"));
    @@ -198,18 +198,18 @@
    // free object
    list->free(list);
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124

    Definition in file qlist.c.

    Function Documentation

    - -

    ◆ qlist()

    + +

    ◆ qlist()

    - + @@ -232,7 +232,7 @@

    +
    qlist_t *list = qlist(0);
    Note
    Available options:
    • QLIST_THREADSAFE - make it thread-safe.
    @@ -242,8 +242,8 @@

    -

    ◆ qlist_setsize()

    + +

    ◆ qlist_setsize()

    @@ -283,8 +283,8 @@

    -

    ◆ qlist_addfirst()

    + +

    ◆ qlist_addfirst()

    @@ -340,15 +340,15 @@

    struct my_obj obj;

    // create a list and add the sample object.
    -
    +
    qlist_t *list = qlist();
    list->addfirst(list, &obj, sizeof(struct my_obj));

    Definition at line 221 of file qlist.c.

    - -

    ◆ qlist_addlast()

    + +

    ◆ qlist_addlast()

    @@ -405,8 +405,8 @@

    -

    ◆ qlist_addat()

    + +

    ◆ qlist_addat()

    qlist_t *list = qlist();
    list->addat(list, 0, &obj, sizeof(obj)); // same as addfirst().
    list->addat(list, -1, &obj, sizeof(obj)); // same as addlast().
    Note
    Index starts from 0.
    @@ -479,14 +479,14 @@

    -

    ◆ qlist_getfirst()

    + +

    ◆ qlist_getfirst()

    qlist_t * qlist qlist_t* qlist ( int  options)
    - + @@ -542,14 +542,14 @@

    -

    ◆ qlist_getlast()

    + +

    ◆ qlist_getlast()

    void * qlist_getfirst void* qlist_getfirst ( qlist_t *  list,
    - + @@ -595,14 +595,14 @@

    -

    ◆ qlist_getat()

    + +

    ◆ qlist_getat()

    void * qlist_getlast void* qlist_getlast ( qlist_t *  list,
    - + @@ -661,14 +661,14 @@

    -

    ◆ qlist_popfirst()

    + +

    ◆ qlist_popfirst()

    void * qlist_getat void* qlist_getat ( qlist_t *  list,
    - + @@ -707,14 +707,14 @@

    -

    ◆ qlist_poplast()

    + +

    ◆ qlist_poplast()

    void * qlist_popfirst void* qlist_popfirst ( qlist_t *  list,
    - + @@ -753,14 +753,14 @@

    -

    ◆ qlist_popat()

    + +

    ◆ qlist_popat()

    void * qlist_poplast void* qlist_poplast ( qlist_t *  list,
    - + @@ -811,8 +811,8 @@

    -

    ◆ qlist_removefirst()

    + +

    ◆ qlist_removefirst()

    @@ -846,8 +846,8 @@

    -

    ◆ qlist_removelast()

    + +

    ◆ qlist_removelast()

    @@ -881,8 +881,8 @@

    -

    ◆ qlist_removeat()

    + +

    ◆ qlist_removeat()

    @@ -927,8 +927,8 @@

    -

    ◆ qlist_getnext()

    + +

    ◆ qlist_getnext()

    @@ -976,7 +976,7 @@

    Note
    obj should be initialized with 0 by using memset() before first call. If newmem flag is true, user should de-allocate obj.name and obj.data resources.
    -
    +
    qlist_t *list = qlist();
    (...add data into list...)
    qlist_obj_t obj;
    @@ -991,8 +991,8 @@

    -

    ◆ qlist_size()

    + +

    ◆ qlist_size()

    @@ -1020,8 +1020,8 @@

    -

    ◆ qlist_datasize()

    + +

    ◆ qlist_datasize()

    @@ -1049,8 +1049,8 @@

    -

    ◆ qlist_reverse()

    + +

    ◆ qlist_reverse()

    @@ -1077,8 +1077,8 @@

    -

    ◆ qlist_clear()

    + +

    ◆ qlist_clear()

    @@ -1105,14 +1105,14 @@

    -

    ◆ qlist_toarray()

    + +

    ◆ qlist_toarray()

    void * qlist_popat void* qlist_popat ( qlist_t *  list,
    - + @@ -1151,14 +1151,14 @@

    -

    ◆ qlist_tostring()

    + +

    ◆ qlist_tostring()

    void * qlist_toarray void* qlist_toarray ( qlist_t *  list,
    - + @@ -1187,8 +1187,8 @@

    -

    ◆ qlist_debug()

    + +

    ◆ qlist_debug()

    @@ -1233,8 +1233,8 @@

    -

    ◆ qlist_lock()

    + +

    ◆ qlist_lock()

    @@ -1262,8 +1262,8 @@

    -

    ◆ qlist_unlock()

    + +

    ◆ qlist_unlock()

    @@ -1290,8 +1290,8 @@

    -

    ◆ qlist_free()

    + +

    ◆ qlist_free()

    @@ -1324,7 +1324,7 @@

    diff --git a/doc/html/qlist_8c.js b/doc/html/qlist_8c.js index 28278e21..cfbced89 100644 --- a/doc/html/qlist_8c.js +++ b/doc/html/qlist_8c.js @@ -1,16 +1,16 @@ var qlist_8c = [ - [ "qlist", "qlist_8c.html#ad9937048b43bf3ced8a61ee16c6e5702", null ], + [ "qlist", "qlist_8c.html#a171f1b505417b5a936e198993c9cb372", null ], [ "qlist_setsize", "qlist_8c.html#ad269a073f8f138a89d91fffccf4878b2", null ], [ "qlist_addfirst", "qlist_8c.html#a362b854c881d11e4ff063bf2415e1202", null ], [ "qlist_addlast", "qlist_8c.html#a5fd1656d6b3534341d7775b201b70f3d", null ], [ "qlist_addat", "qlist_8c.html#a29a98c9eb9a93b096ca15db02010059a", null ], - [ "qlist_getfirst", "qlist_8c.html#a81f8833460fec073d5b49fd87a3a76fa", null ], - [ "qlist_getlast", "qlist_8c.html#a0247388edd52ee3a59485f46e525f5c3", null ], - [ "qlist_getat", "qlist_8c.html#a50b6b9d3f2ffe8230041e38129b5be97", null ], - [ "qlist_popfirst", "qlist_8c.html#ac80dea337388cd5a5ec319ea3aeae739", null ], - [ "qlist_poplast", "qlist_8c.html#a2fd39ba9956df93d34e557905bb41656", null ], - [ "qlist_popat", "qlist_8c.html#a811fd272bfa6c07a5b190c6a1321dd49", null ], + [ "qlist_getfirst", "qlist_8c.html#ae591e0ee1380e484a7707beed87ada6f", null ], + [ "qlist_getlast", "qlist_8c.html#a8aa5812e608102e152a4e6c906fd848c", null ], + [ "qlist_getat", "qlist_8c.html#ae5158e057b797633b43190806fc49ce8", null ], + [ "qlist_popfirst", "qlist_8c.html#ab988e96c223ecdf63105f6797e42ecd9", null ], + [ "qlist_poplast", "qlist_8c.html#a19d80e0634bda2350372bc15f5b92970", null ], + [ "qlist_popat", "qlist_8c.html#a083e4eb3b82834756608e88a15a38164", null ], [ "qlist_removefirst", "qlist_8c.html#a7bbafb0265dbbd962629fbc6beec9a9d", null ], [ "qlist_removelast", "qlist_8c.html#a2148cf9191e31eed686f9c3894ee7fc9", null ], [ "qlist_removeat", "qlist_8c.html#af8fafa8e268392b76496f752d9e010c6", null ], @@ -19,8 +19,8 @@ var qlist_8c = [ "qlist_datasize", "qlist_8c.html#a666371a40b3d4347e0a73add22e90e1a", null ], [ "qlist_reverse", "qlist_8c.html#a18e9f1aaca165a95810f7889ff27088e", null ], [ "qlist_clear", "qlist_8c.html#a8e982d17fa5c0d2d8a2380b54aad7fbd", null ], - [ "qlist_toarray", "qlist_8c.html#aed6f2725e1649367602ad497ef9c86bd", null ], - [ "qlist_tostring", "qlist_8c.html#a13901f1a22643910f8de93d95b3571d8", null ], + [ "qlist_toarray", "qlist_8c.html#acd6f76ee0d811b1c7f9be494bded46f3", null ], + [ "qlist_tostring", "qlist_8c.html#a4a27432727387f75f1118dcadb818bc5", null ], [ "qlist_debug", "qlist_8c.html#a49359e2de05870c5a6b8c1e8445c6865", null ], [ "qlist_lock", "qlist_8c.html#ab4703d7188e6d1e5ca73827fc1aa29ad", null ], [ "qlist_unlock", "qlist_8c.html#a09fa7687257b3ef85eceed06d358e883", null ], diff --git a/doc/html/qlist_8c_source.html b/doc/html/qlist_8c_source.html index f41df336..5f60e987 100644 --- a/doc/html/qlist_8c_source.html +++ b/doc/html/qlist_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qlist.c Source File @@ -20,8 +20,8 @@

    char * qlist_tostring char* qlist_tostring ( qlist_t *  list)
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,999 +52,1000 @@
    -
    qlist.c
    +
    +
    qlist.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qlist.c Doubly Linked-list implementation.
    -
    31 *
    -
    32 * qlist container is a doubly Linked-List implementation.
    -
    33 * qlist provides uniformly named methods to add, get, pop and remove an
    -
    34 * element at the beginning and end of the list. These operations allow qlist
    -
    35 * to be used as a stack, queue, or double-ended queue.
    -
    36 *
    -
    37 * @code
    -
    38 * [Conceptional Data Structure Diagram]
    -
    39 *
    -
    40 * last~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
    -
    41 * |
    -
    42 * +-----------+ doubly +-----------+ doubly +-|---------+
    -
    43 * first~~~|~> 0 <~|~~~~~~~~~~|~> 1 <~|~~~~~~~~~~|~> N |
    -
    44 * +-----|-----+ linked +-----|-----+ linked +-----|-----+
    -
    45 * | | |
    -
    46 * +-----v---------------+ | +-----v-----+
    -
    47 * | DATA A | | | DATA N |
    -
    48 * +---------------------+ | +-----------+
    -
    49 * +---------------------v------------------+
    -
    50 * | DATA B |
    -
    51 * +----------------------------------------+
    -
    52 * @endcode
    -
    53 *
    -
    54 * @code
    -
    55 * // create a list.
    -
    56 * qlist_t *list = qlist(QLIST_THREADSAFE);
    -
    57 *
    -
    58 * // insert elements
    -
    59 * list->addlast(list, "e1", sizeof("e1"));
    -
    60 * list->addlast(list, "e2", sizeof("e2"));
    -
    61 * list->addlast(list, "e3", sizeof("e3"));
    -
    62 *
    -
    63 * // get
    -
    64 * char *e1 = (char*)list->getfirst(list, NULL, true)); // malloced
    -
    65 * char *e3 = (char*)list->getat(list, -1, NULL, false)); // no malloc
    -
    66 * (...omit...)
    -
    67 * free(e1);
    -
    68 *
    -
    69 * // pop (get and remove)
    -
    70 * char *e2 = (char*)list->popat(list, 1, NULL)); // get malloced copy
    -
    71 * (...omit...)
    -
    72 * free(e2);
    -
    73 *
    -
    74 * // debug output
    -
    75 * list->debug(list, stdout, true);
    -
    76 *
    -
    77 * // traversal
    -
    78 * qlist_obj_t obj;
    -
    79 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    80 * list->lock(list);
    -
    81 * while (list->getnext(list, &obj, false) == true) {
    -
    82 * printf("DATA=%s, SIZE=%zu\n", (char*)obj.data, obj.size);
    -
    83 * }
    -
    84 * list->unlock(list);
    -
    85 *
    -
    86 * // free object
    -
    87 * list->free(list);
    -
    88 * @endcode
    -
    89 */
    -
    90
    -
    91#include <stdio.h>
    -
    92#include <stdlib.h>
    -
    93#include <stdbool.h>
    -
    94#include <string.h>
    -
    95#include <errno.h>
    -
    96#include "qinternal.h"
    -
    97#include "containers/qlist.h"
    -
    98
    -
    99#ifndef _DOXYGEN_SKIP
    -
    100
    -
    101static void *get_at(qlist_t *list, int index, size_t *size, bool newmem, bool remove);
    -
    102static qlist_obj_t *get_obj(qlist_t *list, int index);
    -
    103static bool remove_obj(qlist_t *list, qlist_obj_t *obj);
    -
    104
    -
    105#endif
    -
    106
    -
    107/**
    -
    108 * Create new qlist_t linked-list container
    -
    109 *
    -
    110 * @param options combination of initialization options.
    -
    111 *
    -
    112 * @return a pointer of malloced qlist_t container, otherwise returns NULL.
    -
    113 * @retval errno will be set in error condition.
    -
    114 * -ENOMEM : Memory allocation failure.
    -
    115 *
    -
    116 * @code
    -
    117 * qlist_t *list = qlist(0);
    -
    118 * @endcode
    -
    119 *
    -
    120 * @note
    -
    121 * Available options:
    -
    122 * - QLIST_THREADSAFE - make it thread-safe.
    -
    123 */
    -
    124qlist_t *qlist(int options) {
    -
    125 qlist_t *list = (qlist_t *) calloc(1, sizeof(qlist_t));
    -
    126 if (list == NULL) {
    -
    127 errno = ENOMEM;
    -
    128 return NULL;
    -
    129 }
    -
    130
    -
    131 // handle options.
    -
    132 if (options & QLIST_THREADSAFE) {
    -
    133 Q_MUTEX_NEW(list->qmutex, true);
    -
    134 if (list->qmutex == NULL) {
    -
    135 errno = ENOMEM;
    -
    136 free(list);
    -
    137 return NULL;
    -
    138 }
    -
    139 }
    -
    140
    -
    141 // member methods
    -
    142 list->setsize = qlist_setsize;
    -
    143
    -
    144 list->addfirst = qlist_addfirst;
    -
    145 list->addlast = qlist_addlast;
    -
    146 list->addat = qlist_addat;
    -
    147
    -
    148 list->getfirst = qlist_getfirst;
    -
    149 list->getlast = qlist_getlast;
    -
    150 list->getat = qlist_getat;
    -
    151 list->getnext = qlist_getnext;
    -
    152
    -
    153 list->popfirst = qlist_popfirst;
    -
    154 list->poplast = qlist_poplast;
    -
    155 list->popat = qlist_popat;
    -
    156
    -
    157 list->removefirst = qlist_removefirst;
    -
    158 list->removelast = qlist_removelast;
    -
    159 list->removeat = qlist_removeat;
    -
    160
    -
    161 list->reverse = qlist_reverse;
    -
    162 list->clear = qlist_clear;
    -
    163
    -
    164 list->size = qlist_size;
    -
    165 list->datasize = qlist_datasize;
    -
    166
    -
    167 list->toarray = qlist_toarray;
    -
    168 list->tostring = qlist_tostring;
    -
    169 list->debug = qlist_debug;
    -
    170
    -
    171 list->lock = qlist_lock;
    -
    172 list->unlock = qlist_unlock;
    -
    173
    -
    174 list->free = qlist_free;
    -
    175
    -
    176 return list;
    -
    177}
    -
    178
    -
    179/**
    -
    180 * qlist->setsize(): Limit maximum number of elements allowed in this list.
    -
    181 *
    -
    182 * @param list qlist_t container pointer.
    -
    183 * @param max maximum number of elements. 0 means no limit.
    -
    184 *
    -
    185 * @return previous maximum number.
    -
    186 *
    -
    187 * @note
    -
    188 * The default maximum number of elements is unlimited.
    -
    189 */
    -
    190size_t qlist_setsize(qlist_t *list, size_t max) {
    -
    191 qlist_lock(list);
    -
    192 size_t old = list->max;
    -
    193 list->max = max;
    -
    194 qlist_unlock(list);
    -
    195 return old;
    -
    196}
    -
    197
    -
    198/**
    -
    199 * qlist->addfirst(): Inserts a element at the beginning of this list.
    -
    200 *
    -
    201 * @param list qlist_t container pointer.
    -
    202 * @param data a pointer which points data memory.
    -
    203 * @param size size of the data.
    -
    204 *
    -
    205 * @return true if successful, otherwise returns false.
    -
    206 * @retval errno will be set in error condition.
    -
    207 * - ENOBUFS : List full. Only happens when this list has set to have limited
    -
    208 * number of elements.
    -
    209 * - EINVAL : Invalid argument.
    -
    210 * - ENOMEM : Memory allocation failure.
    -
    211 *
    -
    212 * @code
    -
    213 * // create a sample object.
    -
    214 * struct my_obj obj;
    -
    215 *
    -
    216 * // create a list and add the sample object.
    -
    217 * qlist_t *list = qlist();
    -
    218 * list->addfirst(list, &obj, sizeof(struct my_obj));
    -
    219 * @endcode
    -
    220 */
    -
    221bool qlist_addfirst(qlist_t *list, const void *data, size_t size) {
    -
    222 return qlist_addat(list, 0, data, size);
    -
    223}
    -
    224
    -
    225/**
    -
    226 * qlist->addlast(): Appends a element to the end of this list.
    -
    227 *
    -
    228 * @param list qlist_t container pointer.
    -
    229 * @param data a pointer which points data memory.
    -
    230 * @param size size of the data.
    -
    231 *
    -
    232 * @return true if successful, otherwise returns false.
    -
    233 * @retval errno will be set in error condition.
    -
    234 * - ENOBUFS : List full. Only happens when this list has set to have limited
    -
    235 * number of elements.
    -
    236 * - EINVAL : Invalid argument.
    -
    237 * - ENOMEM : Memory allocation failure.
    -
    238 */
    -
    239bool qlist_addlast(qlist_t *list, const void *data, size_t size) {
    -
    240 return qlist_addat(list, -1, data, size);
    -
    241}
    -
    242
    -
    243/**
    -
    244 * qlist->addat(): Inserts a element at the specified position in this
    -
    245 * list.
    -
    246 *
    -
    247 * @param list qlist_t container pointer.
    -
    248 * @param index index at which the specified element is to be inserted.
    -
    249 * @param data a pointer which points data memory.
    -
    250 * @param size size of the data.
    -
    251 *
    -
    252 * @return true if successful, otherwise returns false.
    -
    253 * @retval errno will be set in error condition.
    -
    254 * - ENOBUFS : List full. Only happens when this list has set to have limited
    -
    255 * number of elements.
    -
    256 * - ERANGE : Index out of range.
    -
    257 * - EINVAL : Invalid argument.
    -
    258 * - ENOMEM : Memory allocation failure.
    -
    259 *
    -
    260 * @code
    -
    261 * first last new
    -
    262 * Linked-list [ A ]<=>[ B ]<=>[ C ]?==?[ ]
    -
    263 * (positive index) 0 1 2 3
    -
    264 * (negative index) -3 -2 -1
    -
    265 * @endcode
    -
    266 *
    -
    267 * @code
    -
    268 * qlist_t *list = qlist();
    -
    269 * list->addat(list, 0, &obj, sizeof(obj)); // same as addfirst().
    -
    270 * list->addat(list, -1, &obj, sizeof(obj)); // same as addlast().
    -
    271 * @endcode
    -
    272 *
    -
    273 * @note
    -
    274 * Index starts from 0.
    -
    275 */
    -
    276bool qlist_addat(qlist_t *list, int index, const void *data, size_t size) {
    -
    277 // check arguments
    -
    278 if (data == NULL || size <= 0) {
    -
    279 errno = EINVAL;
    -
    280 return false;
    -
    281 }
    -
    282
    -
    283 qlist_lock(list);
    -
    284
    -
    285 // check maximum number of allowed elements if set
    -
    286 if (list->max > 0 && list->num >= list->max) {
    -
    287 errno = ENOBUFS;
    -
    288 qlist_unlock(list);
    -
    289 return false;
    -
    290 }
    -
    291
    -
    292 // adjust index
    -
    293 if (index < 0)
    -
    294 index = (list->num + index) + 1; // -1 is same as addlast()
    -
    295 if (index < 0 || index > list->num) {
    -
    296 // out of bound
    -
    297 qlist_unlock(list);
    -
    298 errno = ERANGE;
    -
    299 return false;
    -
    300 }
    -
    301
    -
    302 // duplicate object
    -
    303 void *dup_data = malloc(size);
    -
    304 if (dup_data == NULL) {
    -
    305 qlist_unlock(list);
    -
    306 errno = ENOMEM;
    -
    307 return false;
    -
    308 }
    -
    309 memcpy(dup_data, data, size);
    -
    310
    -
    311 // make new object list
    -
    312 qlist_obj_t *obj = (qlist_obj_t *) malloc(sizeof(qlist_obj_t));
    -
    313 if (obj == NULL) {
    -
    314 free(dup_data);
    -
    315 qlist_unlock(list);
    -
    316 errno = ENOMEM;
    -
    317 return false;
    -
    318 }
    -
    319 obj->data = dup_data;
    -
    320 obj->size = size;
    -
    321 obj->prev = NULL;
    -
    322 obj->next = NULL;
    -
    323
    -
    324 // make link
    -
    325 if (index == 0) {
    -
    326 // add at first
    -
    327 obj->next = list->first;
    -
    328 if (obj->next != NULL)
    -
    329 obj->next->prev = obj;
    -
    330 list->first = obj;
    -
    331 if (list->last == NULL)
    -
    332 list->last = obj;
    -
    333 } else if (index == list->num) {
    -
    334 // add after last
    -
    335 obj->prev = list->last;
    -
    336 if (obj->prev != NULL)
    -
    337 obj->prev->next = obj;
    -
    338 list->last = obj;
    -
    339 if (list->first == NULL)
    -
    340 list->first = obj;
    -
    341 } else {
    -
    342 // add at the middle of list
    -
    343 qlist_obj_t *tgt = get_obj(list, index);
    -
    344 if (tgt == NULL) {
    -
    345 // should not be happened.
    -
    346 free(dup_data);
    -
    347 free(obj);
    -
    348 qlist_unlock(list);
    -
    349 errno = EAGAIN;
    -
    350 return false;
    -
    351 }
    -
    352
    -
    353 // insert obj
    -
    354 tgt->prev->next = obj;
    -
    355 obj->prev = tgt->prev;
    -
    356 obj->next = tgt;
    -
    357 tgt->prev = obj;
    -
    358 }
    -
    359
    -
    360 list->datasum += size;
    -
    361 list->num++;
    -
    362
    -
    363 qlist_unlock(list);
    -
    364
    -
    365 return true;
    -
    366}
    -
    367
    -
    368/**
    -
    369 * qlist->getfirst(): Returns the first element in this list.
    -
    370 *
    -
    371 * @param list qlist_t container pointer.
    -
    372 * @param size if size is not NULL, element size will be stored.
    -
    373 * @param newmem whether or not to allocate memory for the element.
    -
    374 *
    -
    375 * @return a pointer of element, otherwise returns NULL.
    -
    376 * @retval errno will be set in error condition.
    -
    377 * - ENOENT : List is empty.
    -
    378 * - ENOMEM : Memory allocation failure.
    -
    379 *
    -
    380 * @code
    -
    381 * size_t size;
    -
    382 * void *data = list->getfirst(list, &size, true);
    -
    383 * if (data != NULL) {
    -
    384 * (...omit...)
    -
    385 * free(data);
    -
    386 * }
    -
    387 * @endcode
    -
    388 */
    -
    389void *qlist_getfirst(qlist_t *list, size_t *size, bool newmem) {
    -
    390 return qlist_getat(list, 0, size, newmem);
    -
    391}
    -
    392
    -
    393/**
    -
    394 * qlist->getlast(): Returns the last element in this list.
    -
    395 *
    -
    396 * @param list qlist_t container pointer.
    -
    397 * @param size if size is not NULL, element size will be stored.
    -
    398 * @param newmem whether or not to allocate memory for the element.
    -
    399 *
    -
    400 * @return a pointer of element, otherwise returns NULL.
    -
    401 * @retval errno will be set in error condition.
    -
    402 * ENOENT : List is empty.
    -
    403 * ENOMEM : Memory allocation failure.
    -
    404 */
    -
    405void *qlist_getlast(qlist_t *list, size_t *size, bool newmem) {
    -
    406 return qlist_getat(list, -1, size, newmem);
    -
    407}
    -
    408
    -
    409/**
    -
    410 * qlist->getat(): Returns the element at the specified position in this
    -
    411 * list.
    -
    412 *
    -
    413 * @param list qlist_t container pointer.
    -
    414 * @param index index at which the specified element is to be inserted
    -
    415 * @param size if size is not NULL, element size will be stored.
    -
    416 * @param newmem whether or not to allocate memory for the element.
    -
    417 *
    -
    418 * @return a pointer of element, otherwise returns NULL.
    -
    419 * @retval errno
    -
    420 * @retval errno will be set in error condition.
    -
    421 * -ERANGE : Index out of range.
    -
    422 * -ENOMEM : Memory allocation failure.
    -
    423 *
    -
    424 * @code
    -
    425 * first last
    -
    426 * Linked-list [ A ]<=>[ B ]<=>[ C ]
    -
    427 * (positive index) 0 1 2
    -
    428 * (negative index) -3 -2 -1
    -
    429 * @endcode
    -
    430 *
    -
    431 * @note
    -
    432 * Negative index can be used for addressing a element from the end in this
    -
    433 * stack. For example, index -1 is same as getlast() and index 0 is same as
    -
    434 * getfirst();
    -
    435 */
    -
    436void *qlist_getat(qlist_t *list, int index, size_t *size, bool newmem) {
    -
    437 return get_at(list, index, size, newmem, false);
    -
    438}
    -
    439
    -
    440/**
    -
    441 * qlist->popfirst(): Returns and remove the first element in this list.
    -
    442 *
    -
    443 * @param list qlist_t container pointer.
    -
    444 * @param size if size is not NULL, element size will be stored.
    -
    445 *
    -
    446 * @return a pointer of malloced element, otherwise returns NULL.
    -
    447 * @retval errno will be set in error condition.
    -
    448 * -ENOENT : List is empty.
    -
    449 * -ENOMEM : Memory allocation failure.
    -
    450 */
    -
    451void *qlist_popfirst(qlist_t *list, size_t *size) {
    -
    452 return qlist_popat(list, 0, size);
    -
    453}
    -
    454
    -
    455/**
    -
    456 * qlist->getlast(): Returns and remove the last element in this list.
    -
    457 *
    -
    458 * @param list qlist_t container pointer.
    -
    459 * @param size if size is not NULL, element size will be stored.
    -
    460 *
    -
    461 * @return a pointer of malloced element, otherwise returns NULL.
    -
    462 * @retval errno will be set in error condition.
    -
    463 * -ENOENT : List is empty.
    -
    464 * -ENOMEM : Memory allocation failure.
    -
    465 */
    -
    466void *qlist_poplast(qlist_t *list, size_t *size) {
    -
    467 return qlist_popat(list, -1, size);
    -
    468}
    -
    469
    -
    470/**
    -
    471 * qlist->popat(): Returns and remove the element at the specified
    -
    472 * position in this list.
    -
    473 *
    -
    474 * @param list qlist_t container pointer.
    -
    475 * @param index index at which the specified element is to be inserted
    -
    476 * @param size if size is not NULL, element size will be stored.
    -
    477 *
    -
    478 * @return a pointer of malloced element, otherwise returns NULL.
    -
    479 * @retval errno will be set in error condition.
    -
    480 * -ERANGE : Index out of range.
    -
    481 * -ENOMEM : Memory allocation failure.
    -
    482 *
    -
    483 * @code
    -
    484 * first last
    -
    485 * Linked-list [ A ]<=>[ B ]<=>[ C ]
    -
    486 * (positive index) 0 1 2
    -
    487 * (negative index) -3 -2 -1
    -
    488 * @endcode
    -
    489 *
    -
    490 * @note
    -
    491 * Negative index can be used for addressing a element from the end in this
    -
    492 * stack. For example, index -1 is same as poplast() and index 0 is same as
    -
    493 * popfirst();
    -
    494 */
    -
    495void *qlist_popat(qlist_t *list, int index, size_t *size) {
    -
    496 return get_at(list, index, size, true, true);
    -
    497}
    -
    498
    -
    499/**
    -
    500 * qlist->removefirst(): Removes the first element in this list.
    -
    501 *
    -
    502 * @param list qlist_t container pointer.
    -
    503 *
    -
    504 * @return a number of removed objects.
    -
    505 * @retval errno will be set in error condition.
    -
    506 * -ENOENT : List is empty.
    -
    507 */
    -
    508bool qlist_removefirst(qlist_t *list) {
    -
    509 return qlist_removeat(list, 0);
    -
    510}
    -
    511
    -
    512/**
    -
    513 * qlist->removelast(): Removes the last element in this list.
    -
    514 *
    -
    515 * @param list qlist_t container pointer.
    -
    516 *
    -
    517 * @return a number of removed objects.
    -
    518 * @retval errno will be set in error condition.
    -
    519 * -ENOENT : List is empty.
    -
    520 */
    -
    521bool qlist_removelast(qlist_t *list) {
    -
    522 return qlist_removeat(list, -1);
    -
    523}
    -
    524
    -
    525/**
    -
    526 * qlist->removeat(): Removes the element at the specified position in
    -
    527 * this list.
    -
    528 *
    -
    529 * @param list qlist_t container pointer.
    -
    530 * @param index index at which the specified element is to be removed.
    -
    531 *
    -
    532 * @return a number of removed objects.
    -
    533 * @retval errno will be set in error condition.
    -
    534 * -ERANGE : Index out of range.
    -
    535 */
    -
    536bool qlist_removeat(qlist_t *list, int index) {
    -
    537 qlist_lock(list);
    -
    538
    -
    539 // get object pointer
    -
    540 qlist_obj_t *obj = get_obj(list, index);
    -
    541 if (obj == NULL) {
    -
    542 qlist_unlock(list);
    -
    543 return false;
    -
    544 }
    -
    545
    -
    546 bool ret = remove_obj(list, obj);
    -
    547
    -
    548 qlist_unlock(list);
    -
    549
    -
    550 return ret;
    -
    551}
    -
    552
    -
    553/**
    -
    554 * qlist->getnext(): Get next element in this list.
    -
    555 *
    -
    556 * @param list qlist_t container pointer.
    -
    557 * @param obj found data will be stored in this structure
    -
    558 * @param newmem whether or not to allocate memory for the element.
    -
    559 *
    -
    560 * @return true if found otherwise returns false
    -
    561 * @retval errno will be set in error condition.
    -
    562 * -ENOENT : No next element.
    -
    563 * -ENOMEM : Memory allocation failure.
    -
    564 *
    -
    565 * @note
    -
    566 * obj should be initialized with 0 by using memset() before first call.
    -
    567 * If newmem flag is true, user should de-allocate obj.name and obj.data
    -
    568 * resources.
    -
    569 *
    -
    570 * @code
    -
    571 * qlist_t *list = qlist();
    -
    572 * (...add data into list...)
    -
    573 *
    -
    574 * qlist_obj_t obj;
    -
    575 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    576 * list->lock(list); // can be omitted in single thread model.
    -
    577 * while (list->getnext(list, &obj, false) == true) {
    -
    578 * printf("DATA=%s, SIZE=%zu\n", (char*)obj.data, obj.size);
    -
    579 * }
    -
    580 * list->unlock(list); // release lock.
    -
    581 * @endcode
    -
    582 */
    -
    583bool qlist_getnext(qlist_t *list, qlist_obj_t *obj, bool newmem) {
    -
    584 if (obj == NULL)
    -
    585 return false;
    -
    586
    -
    587 qlist_lock(list);
    -
    588
    -
    589 qlist_obj_t *cont = NULL;
    -
    590 if (obj->size == 0)
    -
    591 cont = list->first;
    -
    592 else
    -
    593 cont = obj->next;
    -
    594
    -
    595 if (cont == NULL) {
    -
    596 errno = ENOENT;
    -
    597 qlist_unlock(list);
    -
    598 return false;
    -
    599 }
    -
    600
    -
    601 bool ret = false;
    -
    602 while (cont != NULL) {
    -
    603 if (newmem == true) {
    -
    604 obj->data = malloc(cont->size);
    -
    605 if (obj->data == NULL)
    -
    606 break;
    -
    607
    -
    608 memcpy(obj->data, cont->data, cont->size);
    -
    609 } else {
    -
    610 obj->data = cont->data;
    -
    611 }
    -
    612 obj->size = cont->size;
    -
    613 obj->prev = cont->prev;
    -
    614 obj->next = cont->next;
    -
    615
    -
    616 ret = true;
    -
    617 break;
    -
    618 }
    -
    619
    -
    620 qlist_unlock(list);
    -
    621 return ret;
    -
    622}
    -
    623
    -
    624/**
    -
    625 * qlist->size(): Returns the number of elements in this list.
    -
    626 *
    -
    627 * @param list qlist_t container pointer.
    -
    628 *
    -
    629 * @return the number of elements in this list.
    -
    630 */
    -
    631size_t qlist_size(qlist_t *list) {
    -
    632 return list->num;
    -
    633}
    -
    634
    -
    635/**
    -
    636 * qlist->size(): Returns the sum of total element size.
    -
    637 *
    -
    638 * @param list qlist_t container pointer.
    -
    639 *
    -
    640 * @return the sum of total element size.
    -
    641 */
    -
    642size_t qlist_datasize(qlist_t *list) {
    -
    643 return list->datasum;
    -
    644}
    -
    645
    -
    646/**
    -
    647 * qlist->reverse(): Reverse the order of elements.
    -
    648 *
    -
    649 * @param list qlist_t container pointer.
    -
    650 */
    -
    651void qlist_reverse(qlist_t *list) {
    -
    652 qlist_lock(list);
    -
    653 qlist_obj_t *obj;
    -
    654 for (obj = list->first; obj;) {
    -
    655 qlist_obj_t *next = obj->next;
    -
    656 obj->next = obj->prev;
    -
    657 obj->prev = next;
    -
    658 obj = next;
    -
    659 }
    -
    660
    -
    661 obj = list->first;
    -
    662 list->first = list->last;
    -
    663 list->last = obj;
    -
    664
    -
    665 qlist_unlock(list);
    -
    666}
    -
    667
    -
    668/**
    -
    669 * qlist->clear(): Removes all of the elements from this list.
    -
    670 *
    -
    671 * @param list qlist_t container pointer.
    -
    672 */
    -
    673void qlist_clear(qlist_t *list) {
    -
    674 qlist_lock(list);
    -
    675 qlist_obj_t *obj;
    -
    676 for (obj = list->first; obj;) {
    -
    677 qlist_obj_t *next = obj->next;
    -
    678 free(obj->data);
    -
    679 free(obj);
    -
    680 obj = next;
    -
    681 }
    -
    682
    -
    683 list->num = 0;
    -
    684 list->datasum = 0;
    -
    685 list->first = NULL;
    -
    686 list->last = NULL;
    -
    687 qlist_unlock(list);
    -
    688}
    -
    689
    -
    690/**
    -
    691 * qlist->toarray(): Returns the serialized chunk containing all the
    -
    692 * elements in this list.
    -
    693 *
    -
    694 * @param list qlist_t container pointer.
    -
    695 * @param size if size is not NULL, chunk size will be stored.
    -
    696 *
    -
    697 * @return a malloced pointer,
    -
    698 * otherwise(if there is no data to merge) returns NULL.
    -
    699 * @retval errno will be set in error condition.
    -
    700 * -ENOENT : List is empty.
    -
    701 * -ENOMEM : Memory allocation failure.
    -
    702 */
    -
    703void *qlist_toarray(qlist_t *list, size_t *size) {
    -
    704 if (list->num <= 0) {
    -
    705 if (size != NULL)
    -
    706 *size = 0;
    -
    707 errno = ENOENT;
    -
    708 return NULL;
    -
    709 }
    -
    710
    -
    711 qlist_lock(list);
    -
    712
    -
    713 void *chunk = malloc(list->datasum);
    -
    714 if (chunk == NULL) {
    -
    715 qlist_unlock(list);
    -
    716 errno = ENOMEM;
    -
    717 return NULL;
    -
    718 }
    -
    719 void *dp = chunk;
    -
    720
    -
    721 qlist_obj_t *obj;
    -
    722 for (obj = list->first; obj; obj = obj->next) {
    -
    723 memcpy(dp, obj->data, obj->size);
    -
    724 dp += obj->size;
    -
    725 }
    -
    726 qlist_unlock(list);
    -
    727
    -
    728 if (size != NULL)
    -
    729 *size = list->datasum;
    -
    730 return chunk;
    -
    731}
    -
    732
    -
    733/**
    -
    734 * qlist->tostring(): Returns a string representation of this list,
    -
    735 * containing string representation of each element.
    -
    736 *
    -
    737 * @param list qlist_t container pointer.
    -
    738 *
    -
    739 * @return a malloced pointer,
    -
    740 * otherwise(if there is no data to merge) returns NULL.
    -
    741 * @retval errno will be set in error condition.
    -
    742 * -ENOENT : List is empty.
    -
    743 * -ENOMEM : Memory allocation failure.
    -
    744 *
    -
    745 * @note
    -
    746 * Return string is always terminated by '\0'.
    -
    747 */
    -
    748char *qlist_tostring(qlist_t *list) {
    -
    749 if (list->num <= 0) {
    -
    750 errno = ENOENT;
    -
    751 return NULL;
    -
    752 }
    -
    753
    -
    754 qlist_lock(list);
    -
    755
    -
    756 void *chunk = malloc(list->datasum + 1);
    -
    757 if (chunk == NULL) {
    -
    758 qlist_unlock(list);
    -
    759 errno = ENOMEM;
    -
    760 return NULL;
    -
    761 }
    -
    762 void *dp = chunk;
    -
    763
    -
    764 qlist_obj_t *obj;
    -
    765 for (obj = list->first; obj; obj = obj->next) {
    -
    766 size_t size = obj->size;
    -
    767 // do not copy tailing '\0'
    -
    768 if (*(char *) (obj->data + (size - 1)) == '\0')
    -
    769 size -= 1;
    -
    770 memcpy(dp, obj->data, size);
    -
    771 dp += size;
    -
    772 }
    -
    773 *((char *) dp) = '\0';
    -
    774 qlist_unlock(list);
    -
    775
    -
    776 return (char *) chunk;
    -
    777}
    -
    778
    -
    779/**
    -
    780 * qlist->debug(): Prints out stored elements for debugging purpose.
    -
    781 *
    -
    782 * @param list qlist_t container pointer.
    -
    783 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    784 *
    -
    785 * @return true if successful, otherwise returns false.
    -
    786 * @retval errno will be set in error condition.
    -
    787 * -EIO : Invalid output stream.
    -
    788 */
    -
    789bool qlist_debug(qlist_t *list, FILE *out) {
    -
    790 if (out == NULL) {
    -
    791 errno = EIO;
    -
    792 return false;
    -
    793 }
    -
    794
    -
    795 qlist_lock(list);
    -
    796 qlist_obj_t *obj;
    -
    797 int i;
    -
    798 for (i = 0, obj = list->first; obj; obj = obj->next, i++) {
    -
    799 fprintf(out, "%d=", i);
    -
    800 _q_textout(out, obj->data, obj->size, MAX_HUMANOUT);
    -
    801 fprintf(out, " (%zu)\n", obj->size);
    -
    802 }
    -
    803 qlist_unlock(list);
    -
    804
    -
    805 return true;
    -
    806}
    -
    807
    -
    808/**
    -
    809 * qlist->lock(): Enters critical section.
    -
    810 *
    -
    811 * @param list qlist_t container pointer.
    -
    812 *
    -
    813 * @note
    -
    814 * From user side, normally locking operation is only needed when traverse all
    -
    815 * elements using qlist->getnext().
    -
    816 */
    -
    817void qlist_lock(qlist_t *list) {
    -
    818 Q_MUTEX_ENTER(list->qmutex);
    -
    819}
    -
    820
    -
    821/**
    -
    822 * qlist->unlock(): Leaves critical section.
    -
    823 *
    -
    824 * @param list qlist_t container pointer.
    -
    825 */
    -
    826void qlist_unlock(qlist_t *list) {
    -
    827 Q_MUTEX_LEAVE(list->qmutex);
    -
    828}
    -
    829
    -
    830/**
    -
    831 * qlist->free(): Free qlist_t.
    -
    832 *
    -
    833 * @param list qlist_t container pointer.
    -
    834 */
    -
    835void qlist_free(qlist_t *list) {
    -
    836 qlist_clear(list);
    -
    837 Q_MUTEX_DESTROY(list->qmutex);
    -
    838
    -
    839 free(list);
    -
    840}
    -
    841
    -
    842#ifndef _DOXYGEN_SKIP
    -
    843
    -
    844static void *get_at(qlist_t *list, int index, size_t *size, bool newmem,
    -
    845bool remove) {
    -
    846 qlist_lock(list);
    -
    847
    -
    848 // get object pointer
    -
    849 qlist_obj_t *obj = get_obj(list, index);
    -
    850 if (obj == NULL) {
    -
    851 qlist_unlock(list);
    -
    852 return false;
    -
    853 }
    -
    854
    -
    855 // copy data
    -
    856 void *data;
    -
    857 if (newmem == true) {
    -
    858 data = malloc(obj->size);
    -
    859 if (data == NULL) {
    -
    860 qlist_unlock(list);
    -
    861 errno = ENOMEM;
    -
    862 return false;
    -
    863 }
    -
    864 memcpy(data, obj->data, obj->size);
    -
    865 } else {
    -
    866 data = obj->data;
    -
    867 }
    -
    868 if (size != NULL)
    -
    869 *size = obj->size;
    -
    870
    -
    871 // remove if necessary
    -
    872 if (remove == true) {
    -
    873 if (remove_obj(list, obj) == false) {
    -
    874 if (newmem == true)
    -
    875 free(data);
    -
    876 data = NULL;
    -
    877 }
    -
    878 }
    -
    879
    -
    880 qlist_unlock(list);
    -
    881
    -
    882 return data;
    -
    883}
    -
    884
    -
    885static qlist_obj_t *get_obj(qlist_t *list, int index) {
    -
    886 // index adjustment
    -
    887 if (index < 0)
    -
    888 index = list->num + index;
    -
    889 if (index >= list->num) {
    -
    890 errno = ERANGE;
    -
    891 return NULL;
    -
    892 }
    -
    893
    -
    894 // detect faster scan direction
    -
    895 bool backward;
    -
    896 qlist_obj_t *obj;
    -
    897 int listidx;
    -
    898 if (index < list->num / 2) {
    -
    899 backward = false;
    -
    900 obj = list->first;
    -
    901 listidx = 0;
    -
    902 } else {
    -
    903 backward = true;
    -
    904 obj = list->last;
    -
    905 listidx = list->num - 1;
    -
    906 }
    -
    907
    -
    908 // find object
    -
    909 while (obj != NULL) {
    -
    910 if (listidx == index)
    -
    911 return obj;
    -
    912
    -
    913 if (backward == false) {
    -
    914 obj = obj->next;
    -
    915 listidx++;
    -
    916 } else {
    -
    917 obj = obj->prev;
    -
    918 listidx--;
    -
    919 }
    -
    920 }
    -
    921
    -
    922 // never reach here
    -
    923 errno = ENOENT;
    -
    924 return NULL;
    -
    925}
    -
    926
    -
    927static bool remove_obj(qlist_t *list, qlist_obj_t *obj) {
    -
    928 if (obj == NULL)
    -
    929 return false;
    -
    930
    -
    931 // chain prev and next elements
    -
    932 if (obj->prev == NULL)
    -
    933 list->first = obj->next;
    -
    934 else
    -
    935 obj->prev->next = obj->next;
    -
    936 if (obj->next == NULL)
    -
    937 list->last = obj->prev;
    -
    938 else
    -
    939 obj->next->prev = obj->prev;
    -
    940
    -
    941 // adjust counter
    -
    942 list->datasum -= obj->size;
    -
    943 list->num--;
    -
    944
    -
    945 // release obj
    -
    946 free(obj->data);
    -
    947 free(obj);
    -
    948
    -
    949 return true;
    -
    950}
    -
    951
    -
    952#endif /* _DOXYGEN_SKIP */
    -
    void * qlist_getlast(qlist_t *list, size_t *size, bool newmem)
    qlist->getlast(): Returns the last element in this list.
    Definition qlist.c:405
    -
    bool qlist_getnext(qlist_t *list, qlist_obj_t *obj, bool newmem)
    qlist->getnext(): Get next element in this list.
    Definition qlist.c:583
    -
    void qlist_unlock(qlist_t *list)
    qlist->unlock(): Leaves critical section.
    Definition qlist.c:826
    -
    char * qlist_tostring(qlist_t *list)
    qlist->tostring(): Returns a string representation of this list, containing string representation of ...
    Definition qlist.c:748
    -
    void qlist_reverse(qlist_t *list)
    qlist->reverse(): Reverse the order of elements.
    Definition qlist.c:651
    -
    bool qlist_removelast(qlist_t *list)
    qlist->removelast(): Removes the last element in this list.
    Definition qlist.c:521
    -
    bool qlist_addat(qlist_t *list, int index, const void *data, size_t size)
    qlist->addat(): Inserts a element at the specified position in this list.
    Definition qlist.c:276
    -
    void * qlist_poplast(qlist_t *list, size_t *size)
    qlist->getlast(): Returns and remove the last element in this list.
    Definition qlist.c:466
    -
    bool qlist_addfirst(qlist_t *list, const void *data, size_t size)
    qlist->addfirst(): Inserts a element at the beginning of this list.
    Definition qlist.c:221
    -
    void qlist_free(qlist_t *list)
    qlist->free(): Free qlist_t.
    Definition qlist.c:835
    -
    bool qlist_debug(qlist_t *list, FILE *out)
    qlist->debug(): Prints out stored elements for debugging purpose.
    Definition qlist.c:789
    -
    void * qlist_getat(qlist_t *list, int index, size_t *size, bool newmem)
    qlist->getat(): Returns the element at the specified position in this list.
    Definition qlist.c:436
    -
    bool qlist_addlast(qlist_t *list, const void *data, size_t size)
    qlist->addlast(): Appends a element to the end of this list.
    Definition qlist.c:239
    -
    size_t qlist_datasize(qlist_t *list)
    qlist->size(): Returns the sum of total element size.
    Definition qlist.c:642
    -
    bool qlist_removefirst(qlist_t *list)
    qlist->removefirst(): Removes the first element in this list.
    Definition qlist.c:508
    -
    size_t qlist_size(qlist_t *list)
    qlist->size(): Returns the number of elements in this list.
    Definition qlist.c:631
    -
    void * qlist_popat(qlist_t *list, int index, size_t *size)
    qlist->popat(): Returns and remove the element at the specified position in this list.
    Definition qlist.c:495
    -
    void * qlist_getfirst(qlist_t *list, size_t *size, bool newmem)
    qlist->getfirst(): Returns the first element in this list.
    Definition qlist.c:389
    -
    void qlist_clear(qlist_t *list)
    qlist->clear(): Removes all of the elements from this list.
    Definition qlist.c:673
    -
    void qlist_lock(qlist_t *list)
    qlist->lock(): Enters critical section.
    Definition qlist.c:817
    -
    void * qlist_popfirst(qlist_t *list, size_t *size)
    qlist->popfirst(): Returns and remove the first element in this list.
    Definition qlist.c:451
    -
    size_t qlist_setsize(qlist_t *list, size_t max)
    qlist->setsize(): Limit maximum number of elements allowed in this list.
    Definition qlist.c:190
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    -
    void * qlist_toarray(qlist_t *list, size_t *size)
    qlist->toarray(): Returns the serialized chunk containing all the elements in this list.
    Definition qlist.c:703
    -
    bool qlist_removeat(qlist_t *list, int index)
    qlist->removeat(): Removes the element at the specified position in this list.
    Definition qlist.c:536
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qlist.c Doubly Linked-list implementation.
    +
    31  *
    +
    32  * qlist container is a doubly Linked-List implementation.
    +
    33  * qlist provides uniformly named methods to add, get, pop and remove an
    +
    34  * element at the beginning and end of the list. These operations allow qlist
    +
    35  * to be used as a stack, queue, or double-ended queue.
    +
    36  *
    +
    37  * @code
    +
    38  * [Conceptional Data Structure Diagram]
    +
    39  *
    +
    40  * last~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
    +
    41  * |
    +
    42  * +-----------+ doubly +-----------+ doubly +-|---------+
    +
    43  * first~~~|~> 0 <~|~~~~~~~~~~|~> 1 <~|~~~~~~~~~~|~> N |
    +
    44  * +-----|-----+ linked +-----|-----+ linked +-----|-----+
    +
    45  * | | |
    +
    46  * +-----v---------------+ | +-----v-----+
    +
    47  * | DATA A | | | DATA N |
    +
    48  * +---------------------+ | +-----------+
    +
    49  * +---------------------v------------------+
    +
    50  * | DATA B |
    +
    51  * +----------------------------------------+
    +
    52  * @endcode
    +
    53  *
    +
    54  * @code
    +
    55  * // create a list.
    +
    56  * qlist_t *list = qlist(QLIST_THREADSAFE);
    +
    57  *
    +
    58  * // insert elements
    +
    59  * list->addlast(list, "e1", sizeof("e1"));
    +
    60  * list->addlast(list, "e2", sizeof("e2"));
    +
    61  * list->addlast(list, "e3", sizeof("e3"));
    +
    62  *
    +
    63  * // get
    +
    64  * char *e1 = (char*)list->getfirst(list, NULL, true)); // malloced
    +
    65  * char *e3 = (char*)list->getat(list, -1, NULL, false)); // no malloc
    +
    66  * (...omit...)
    +
    67  * free(e1);
    +
    68  *
    +
    69  * // pop (get and remove)
    +
    70  * char *e2 = (char*)list->popat(list, 1, NULL)); // get malloced copy
    +
    71  * (...omit...)
    +
    72  * free(e2);
    +
    73  *
    +
    74  * // debug output
    +
    75  * list->debug(list, stdout, true);
    +
    76  *
    +
    77  * // traversal
    +
    78  * qlist_obj_t obj;
    +
    79  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    80  * list->lock(list);
    +
    81  * while (list->getnext(list, &obj, false) == true) {
    +
    82  * printf("DATA=%s, SIZE=%zu\n", (char*)obj.data, obj.size);
    +
    83  * }
    +
    84  * list->unlock(list);
    +
    85  *
    +
    86  * // free object
    +
    87  * list->free(list);
    +
    88  * @endcode
    +
    89  */
    +
    90 
    +
    91 #include <stdio.h>
    +
    92 #include <stdlib.h>
    +
    93 #include <stdbool.h>
    +
    94 #include <string.h>
    +
    95 #include <errno.h>
    +
    96 #include "qinternal.h"
    +
    97 #include "containers/qlist.h"
    +
    98 
    +
    99 #ifndef _DOXYGEN_SKIP
    +
    100 
    +
    101 static void *get_at(qlist_t *list, int index, size_t *size, bool newmem, bool remove);
    +
    102 static qlist_obj_t *get_obj(qlist_t *list, int index);
    +
    103 static bool remove_obj(qlist_t *list, qlist_obj_t *obj);
    +
    104 
    +
    105 #endif
    +
    106 
    +
    107 /**
    +
    108  * Create new qlist_t linked-list container
    +
    109  *
    +
    110  * @param options combination of initialization options.
    +
    111  *
    +
    112  * @return a pointer of malloced qlist_t container, otherwise returns NULL.
    +
    113  * @retval errno will be set in error condition.
    +
    114  * -ENOMEM : Memory allocation failure.
    +
    115  *
    +
    116  * @code
    +
    117  * qlist_t *list = qlist(0);
    +
    118  * @endcode
    +
    119  *
    +
    120  * @note
    +
    121  * Available options:
    +
    122  * - QLIST_THREADSAFE - make it thread-safe.
    +
    123  */
    +
    124 qlist_t *qlist(int options) {
    +
    125  qlist_t *list = (qlist_t *) calloc(1, sizeof(qlist_t));
    +
    126  if (list == NULL) {
    +
    127  errno = ENOMEM;
    +
    128  return NULL;
    +
    129  }
    +
    130 
    +
    131  // handle options.
    +
    132  if (options & QLIST_THREADSAFE) {
    +
    133  Q_MUTEX_NEW(list->qmutex, true);
    +
    134  if (list->qmutex == NULL) {
    +
    135  errno = ENOMEM;
    +
    136  free(list);
    +
    137  return NULL;
    +
    138  }
    +
    139  }
    +
    140 
    +
    141  // member methods
    +
    142  list->setsize = qlist_setsize;
    +
    143 
    +
    144  list->addfirst = qlist_addfirst;
    +
    145  list->addlast = qlist_addlast;
    +
    146  list->addat = qlist_addat;
    +
    147 
    +
    148  list->getfirst = qlist_getfirst;
    +
    149  list->getlast = qlist_getlast;
    +
    150  list->getat = qlist_getat;
    +
    151  list->getnext = qlist_getnext;
    +
    152 
    +
    153  list->popfirst = qlist_popfirst;
    +
    154  list->poplast = qlist_poplast;
    +
    155  list->popat = qlist_popat;
    +
    156 
    +
    157  list->removefirst = qlist_removefirst;
    +
    158  list->removelast = qlist_removelast;
    +
    159  list->removeat = qlist_removeat;
    +
    160 
    +
    161  list->reverse = qlist_reverse;
    +
    162  list->clear = qlist_clear;
    +
    163 
    +
    164  list->size = qlist_size;
    +
    165  list->datasize = qlist_datasize;
    +
    166 
    +
    167  list->toarray = qlist_toarray;
    +
    168  list->tostring = qlist_tostring;
    +
    169  list->debug = qlist_debug;
    +
    170 
    +
    171  list->lock = qlist_lock;
    +
    172  list->unlock = qlist_unlock;
    +
    173 
    +
    174  list->free = qlist_free;
    +
    175 
    +
    176  return list;
    +
    177 }
    +
    178 
    +
    179 /**
    +
    180  * qlist->setsize(): Limit maximum number of elements allowed in this list.
    +
    181  *
    +
    182  * @param list qlist_t container pointer.
    +
    183  * @param max maximum number of elements. 0 means no limit.
    +
    184  *
    +
    185  * @return previous maximum number.
    +
    186  *
    +
    187  * @note
    +
    188  * The default maximum number of elements is unlimited.
    +
    189  */
    +
    190 size_t qlist_setsize(qlist_t *list, size_t max) {
    +
    191  qlist_lock(list);
    +
    192  size_t old = list->max;
    +
    193  list->max = max;
    +
    194  qlist_unlock(list);
    +
    195  return old;
    +
    196 }
    +
    197 
    +
    198 /**
    +
    199  * qlist->addfirst(): Inserts a element at the beginning of this list.
    +
    200  *
    +
    201  * @param list qlist_t container pointer.
    +
    202  * @param data a pointer which points data memory.
    +
    203  * @param size size of the data.
    +
    204  *
    +
    205  * @return true if successful, otherwise returns false.
    +
    206  * @retval errno will be set in error condition.
    +
    207  * - ENOBUFS : List full. Only happens when this list has set to have limited
    +
    208  * number of elements.
    +
    209  * - EINVAL : Invalid argument.
    +
    210  * - ENOMEM : Memory allocation failure.
    +
    211  *
    +
    212  * @code
    +
    213  * // create a sample object.
    +
    214  * struct my_obj obj;
    +
    215  *
    +
    216  * // create a list and add the sample object.
    +
    217  * qlist_t *list = qlist();
    +
    218  * list->addfirst(list, &obj, sizeof(struct my_obj));
    +
    219  * @endcode
    +
    220  */
    +
    221 bool qlist_addfirst(qlist_t *list, const void *data, size_t size) {
    +
    222  return qlist_addat(list, 0, data, size);
    +
    223 }
    +
    224 
    +
    225 /**
    +
    226  * qlist->addlast(): Appends a element to the end of this list.
    +
    227  *
    +
    228  * @param list qlist_t container pointer.
    +
    229  * @param data a pointer which points data memory.
    +
    230  * @param size size of the data.
    +
    231  *
    +
    232  * @return true if successful, otherwise returns false.
    +
    233  * @retval errno will be set in error condition.
    +
    234  * - ENOBUFS : List full. Only happens when this list has set to have limited
    +
    235  * number of elements.
    +
    236  * - EINVAL : Invalid argument.
    +
    237  * - ENOMEM : Memory allocation failure.
    +
    238  */
    +
    239 bool qlist_addlast(qlist_t *list, const void *data, size_t size) {
    +
    240  return qlist_addat(list, -1, data, size);
    +
    241 }
    +
    242 
    +
    243 /**
    +
    244  * qlist->addat(): Inserts a element at the specified position in this
    +
    245  * list.
    +
    246  *
    +
    247  * @param list qlist_t container pointer.
    +
    248  * @param index index at which the specified element is to be inserted.
    +
    249  * @param data a pointer which points data memory.
    +
    250  * @param size size of the data.
    +
    251  *
    +
    252  * @return true if successful, otherwise returns false.
    +
    253  * @retval errno will be set in error condition.
    +
    254  * - ENOBUFS : List full. Only happens when this list has set to have limited
    +
    255  * number of elements.
    +
    256  * - ERANGE : Index out of range.
    +
    257  * - EINVAL : Invalid argument.
    +
    258  * - ENOMEM : Memory allocation failure.
    +
    259  *
    +
    260  * @code
    +
    261  * first last new
    +
    262  * Linked-list [ A ]<=>[ B ]<=>[ C ]?==?[ ]
    +
    263  * (positive index) 0 1 2 3
    +
    264  * (negative index) -3 -2 -1
    +
    265  * @endcode
    +
    266  *
    +
    267  * @code
    +
    268  * qlist_t *list = qlist();
    +
    269  * list->addat(list, 0, &obj, sizeof(obj)); // same as addfirst().
    +
    270  * list->addat(list, -1, &obj, sizeof(obj)); // same as addlast().
    +
    271  * @endcode
    +
    272  *
    +
    273  * @note
    +
    274  * Index starts from 0.
    +
    275  */
    +
    276 bool qlist_addat(qlist_t *list, int index, const void *data, size_t size) {
    +
    277  // check arguments
    +
    278  if (data == NULL || size <= 0) {
    +
    279  errno = EINVAL;
    +
    280  return false;
    +
    281  }
    +
    282 
    +
    283  qlist_lock(list);
    +
    284 
    +
    285  // check maximum number of allowed elements if set
    +
    286  if (list->max > 0 && list->num >= list->max) {
    +
    287  errno = ENOBUFS;
    +
    288  qlist_unlock(list);
    +
    289  return false;
    +
    290  }
    +
    291 
    +
    292  // adjust index
    +
    293  if (index < 0)
    +
    294  index = (list->num + index) + 1; // -1 is same as addlast()
    +
    295  if (index < 0 || index > list->num) {
    +
    296  // out of bound
    +
    297  qlist_unlock(list);
    +
    298  errno = ERANGE;
    +
    299  return false;
    +
    300  }
    +
    301 
    +
    302  // duplicate object
    +
    303  void *dup_data = malloc(size);
    +
    304  if (dup_data == NULL) {
    +
    305  qlist_unlock(list);
    +
    306  errno = ENOMEM;
    +
    307  return false;
    +
    308  }
    +
    309  memcpy(dup_data, data, size);
    +
    310 
    +
    311  // make new object list
    +
    312  qlist_obj_t *obj = (qlist_obj_t *) malloc(sizeof(qlist_obj_t));
    +
    313  if (obj == NULL) {
    +
    314  free(dup_data);
    +
    315  qlist_unlock(list);
    +
    316  errno = ENOMEM;
    +
    317  return false;
    +
    318  }
    +
    319  obj->data = dup_data;
    +
    320  obj->size = size;
    +
    321  obj->prev = NULL;
    +
    322  obj->next = NULL;
    +
    323 
    +
    324  // make link
    +
    325  if (index == 0) {
    +
    326  // add at first
    +
    327  obj->next = list->first;
    +
    328  if (obj->next != NULL)
    +
    329  obj->next->prev = obj;
    +
    330  list->first = obj;
    +
    331  if (list->last == NULL)
    +
    332  list->last = obj;
    +
    333  } else if (index == list->num) {
    +
    334  // add after last
    +
    335  obj->prev = list->last;
    +
    336  if (obj->prev != NULL)
    +
    337  obj->prev->next = obj;
    +
    338  list->last = obj;
    +
    339  if (list->first == NULL)
    +
    340  list->first = obj;
    +
    341  } else {
    +
    342  // add at the middle of list
    +
    343  qlist_obj_t *tgt = get_obj(list, index);
    +
    344  if (tgt == NULL) {
    +
    345  // should not be happened.
    +
    346  free(dup_data);
    +
    347  free(obj);
    +
    348  qlist_unlock(list);
    +
    349  errno = EAGAIN;
    +
    350  return false;
    +
    351  }
    +
    352 
    +
    353  // insert obj
    +
    354  tgt->prev->next = obj;
    +
    355  obj->prev = tgt->prev;
    +
    356  obj->next = tgt;
    +
    357  tgt->prev = obj;
    +
    358  }
    +
    359 
    +
    360  list->datasum += size;
    +
    361  list->num++;
    +
    362 
    +
    363  qlist_unlock(list);
    +
    364 
    +
    365  return true;
    +
    366 }
    +
    367 
    +
    368 /**
    +
    369  * qlist->getfirst(): Returns the first element in this list.
    +
    370  *
    +
    371  * @param list qlist_t container pointer.
    +
    372  * @param size if size is not NULL, element size will be stored.
    +
    373  * @param newmem whether or not to allocate memory for the element.
    +
    374  *
    +
    375  * @return a pointer of element, otherwise returns NULL.
    +
    376  * @retval errno will be set in error condition.
    +
    377  * - ENOENT : List is empty.
    +
    378  * - ENOMEM : Memory allocation failure.
    +
    379  *
    +
    380  * @code
    +
    381  * size_t size;
    +
    382  * void *data = list->getfirst(list, &size, true);
    +
    383  * if (data != NULL) {
    +
    384  * (...omit...)
    +
    385  * free(data);
    +
    386  * }
    +
    387  * @endcode
    +
    388  */
    +
    389 void *qlist_getfirst(qlist_t *list, size_t *size, bool newmem) {
    +
    390  return qlist_getat(list, 0, size, newmem);
    +
    391 }
    +
    392 
    +
    393 /**
    +
    394  * qlist->getlast(): Returns the last element in this list.
    +
    395  *
    +
    396  * @param list qlist_t container pointer.
    +
    397  * @param size if size is not NULL, element size will be stored.
    +
    398  * @param newmem whether or not to allocate memory for the element.
    +
    399  *
    +
    400  * @return a pointer of element, otherwise returns NULL.
    +
    401  * @retval errno will be set in error condition.
    +
    402  * ENOENT : List is empty.
    +
    403  * ENOMEM : Memory allocation failure.
    +
    404  */
    +
    405 void *qlist_getlast(qlist_t *list, size_t *size, bool newmem) {
    +
    406  return qlist_getat(list, -1, size, newmem);
    +
    407 }
    +
    408 
    +
    409 /**
    +
    410  * qlist->getat(): Returns the element at the specified position in this
    +
    411  * list.
    +
    412  *
    +
    413  * @param list qlist_t container pointer.
    +
    414  * @param index index at which the specified element is to be inserted
    +
    415  * @param size if size is not NULL, element size will be stored.
    +
    416  * @param newmem whether or not to allocate memory for the element.
    +
    417  *
    +
    418  * @return a pointer of element, otherwise returns NULL.
    +
    419  * @retval errno
    +
    420  * @retval errno will be set in error condition.
    +
    421  * -ERANGE : Index out of range.
    +
    422  * -ENOMEM : Memory allocation failure.
    +
    423  *
    +
    424  * @code
    +
    425  * first last
    +
    426  * Linked-list [ A ]<=>[ B ]<=>[ C ]
    +
    427  * (positive index) 0 1 2
    +
    428  * (negative index) -3 -2 -1
    +
    429  * @endcode
    +
    430  *
    +
    431  * @note
    +
    432  * Negative index can be used for addressing a element from the end in this
    +
    433  * stack. For example, index -1 is same as getlast() and index 0 is same as
    +
    434  * getfirst();
    +
    435  */
    +
    436 void *qlist_getat(qlist_t *list, int index, size_t *size, bool newmem) {
    +
    437  return get_at(list, index, size, newmem, false);
    +
    438 }
    +
    439 
    +
    440 /**
    +
    441  * qlist->popfirst(): Returns and remove the first element in this list.
    +
    442  *
    +
    443  * @param list qlist_t container pointer.
    +
    444  * @param size if size is not NULL, element size will be stored.
    +
    445  *
    +
    446  * @return a pointer of malloced element, otherwise returns NULL.
    +
    447  * @retval errno will be set in error condition.
    +
    448  * -ENOENT : List is empty.
    +
    449  * -ENOMEM : Memory allocation failure.
    +
    450  */
    +
    451 void *qlist_popfirst(qlist_t *list, size_t *size) {
    +
    452  return qlist_popat(list, 0, size);
    +
    453 }
    +
    454 
    +
    455 /**
    +
    456  * qlist->getlast(): Returns and remove the last element in this list.
    +
    457  *
    +
    458  * @param list qlist_t container pointer.
    +
    459  * @param size if size is not NULL, element size will be stored.
    +
    460  *
    +
    461  * @return a pointer of malloced element, otherwise returns NULL.
    +
    462  * @retval errno will be set in error condition.
    +
    463  * -ENOENT : List is empty.
    +
    464  * -ENOMEM : Memory allocation failure.
    +
    465  */
    +
    466 void *qlist_poplast(qlist_t *list, size_t *size) {
    +
    467  return qlist_popat(list, -1, size);
    +
    468 }
    +
    469 
    +
    470 /**
    +
    471  * qlist->popat(): Returns and remove the element at the specified
    +
    472  * position in this list.
    +
    473  *
    +
    474  * @param list qlist_t container pointer.
    +
    475  * @param index index at which the specified element is to be inserted
    +
    476  * @param size if size is not NULL, element size will be stored.
    +
    477  *
    +
    478  * @return a pointer of malloced element, otherwise returns NULL.
    +
    479  * @retval errno will be set in error condition.
    +
    480  * -ERANGE : Index out of range.
    +
    481  * -ENOMEM : Memory allocation failure.
    +
    482  *
    +
    483  * @code
    +
    484  * first last
    +
    485  * Linked-list [ A ]<=>[ B ]<=>[ C ]
    +
    486  * (positive index) 0 1 2
    +
    487  * (negative index) -3 -2 -1
    +
    488  * @endcode
    +
    489  *
    +
    490  * @note
    +
    491  * Negative index can be used for addressing a element from the end in this
    +
    492  * stack. For example, index -1 is same as poplast() and index 0 is same as
    +
    493  * popfirst();
    +
    494  */
    +
    495 void *qlist_popat(qlist_t *list, int index, size_t *size) {
    +
    496  return get_at(list, index, size, true, true);
    +
    497 }
    +
    498 
    +
    499 /**
    +
    500  * qlist->removefirst(): Removes the first element in this list.
    +
    501  *
    +
    502  * @param list qlist_t container pointer.
    +
    503  *
    +
    504  * @return a number of removed objects.
    +
    505  * @retval errno will be set in error condition.
    +
    506  * -ENOENT : List is empty.
    +
    507  */
    +
    508 bool qlist_removefirst(qlist_t *list) {
    +
    509  return qlist_removeat(list, 0);
    +
    510 }
    +
    511 
    +
    512 /**
    +
    513  * qlist->removelast(): Removes the last element in this list.
    +
    514  *
    +
    515  * @param list qlist_t container pointer.
    +
    516  *
    +
    517  * @return a number of removed objects.
    +
    518  * @retval errno will be set in error condition.
    +
    519  * -ENOENT : List is empty.
    +
    520  */
    +
    521 bool qlist_removelast(qlist_t *list) {
    +
    522  return qlist_removeat(list, -1);
    +
    523 }
    +
    524 
    +
    525 /**
    +
    526  * qlist->removeat(): Removes the element at the specified position in
    +
    527  * this list.
    +
    528  *
    +
    529  * @param list qlist_t container pointer.
    +
    530  * @param index index at which the specified element is to be removed.
    +
    531  *
    +
    532  * @return a number of removed objects.
    +
    533  * @retval errno will be set in error condition.
    +
    534  * -ERANGE : Index out of range.
    +
    535  */
    +
    536 bool qlist_removeat(qlist_t *list, int index) {
    +
    537  qlist_lock(list);
    +
    538 
    +
    539  // get object pointer
    +
    540  qlist_obj_t *obj = get_obj(list, index);
    +
    541  if (obj == NULL) {
    +
    542  qlist_unlock(list);
    +
    543  return false;
    +
    544  }
    +
    545 
    +
    546  bool ret = remove_obj(list, obj);
    +
    547 
    +
    548  qlist_unlock(list);
    +
    549 
    +
    550  return ret;
    +
    551 }
    +
    552 
    +
    553 /**
    +
    554  * qlist->getnext(): Get next element in this list.
    +
    555  *
    +
    556  * @param list qlist_t container pointer.
    +
    557  * @param obj found data will be stored in this structure
    +
    558  * @param newmem whether or not to allocate memory for the element.
    +
    559  *
    +
    560  * @return true if found otherwise returns false
    +
    561  * @retval errno will be set in error condition.
    +
    562  * -ENOENT : No next element.
    +
    563  * -ENOMEM : Memory allocation failure.
    +
    564  *
    +
    565  * @note
    +
    566  * obj should be initialized with 0 by using memset() before first call.
    +
    567  * If newmem flag is true, user should de-allocate obj.name and obj.data
    +
    568  * resources.
    +
    569  *
    +
    570  * @code
    +
    571  * qlist_t *list = qlist();
    +
    572  * (...add data into list...)
    +
    573  *
    +
    574  * qlist_obj_t obj;
    +
    575  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    576  * list->lock(list); // can be omitted in single thread model.
    +
    577  * while (list->getnext(list, &obj, false) == true) {
    +
    578  * printf("DATA=%s, SIZE=%zu\n", (char*)obj.data, obj.size);
    +
    579  * }
    +
    580  * list->unlock(list); // release lock.
    +
    581  * @endcode
    +
    582  */
    +
    583 bool qlist_getnext(qlist_t *list, qlist_obj_t *obj, bool newmem) {
    +
    584  if (obj == NULL)
    +
    585  return false;
    +
    586 
    +
    587  qlist_lock(list);
    +
    588 
    +
    589  qlist_obj_t *cont = NULL;
    +
    590  if (obj->size == 0)
    +
    591  cont = list->first;
    +
    592  else
    +
    593  cont = obj->next;
    +
    594 
    +
    595  if (cont == NULL) {
    +
    596  errno = ENOENT;
    +
    597  qlist_unlock(list);
    +
    598  return false;
    +
    599  }
    +
    600 
    +
    601  bool ret = false;
    +
    602  while (cont != NULL) {
    +
    603  if (newmem == true) {
    +
    604  obj->data = malloc(cont->size);
    +
    605  if (obj->data == NULL)
    +
    606  break;
    +
    607 
    +
    608  memcpy(obj->data, cont->data, cont->size);
    +
    609  } else {
    +
    610  obj->data = cont->data;
    +
    611  }
    +
    612  obj->size = cont->size;
    +
    613  obj->prev = cont->prev;
    +
    614  obj->next = cont->next;
    +
    615 
    +
    616  ret = true;
    +
    617  break;
    +
    618  }
    +
    619 
    +
    620  qlist_unlock(list);
    +
    621  return ret;
    +
    622 }
    +
    623 
    +
    624 /**
    +
    625  * qlist->size(): Returns the number of elements in this list.
    +
    626  *
    +
    627  * @param list qlist_t container pointer.
    +
    628  *
    +
    629  * @return the number of elements in this list.
    +
    630  */
    +
    631 size_t qlist_size(qlist_t *list) {
    +
    632  return list->num;
    +
    633 }
    +
    634 
    +
    635 /**
    +
    636  * qlist->size(): Returns the sum of total element size.
    +
    637  *
    +
    638  * @param list qlist_t container pointer.
    +
    639  *
    +
    640  * @return the sum of total element size.
    +
    641  */
    +
    642 size_t qlist_datasize(qlist_t *list) {
    +
    643  return list->datasum;
    +
    644 }
    +
    645 
    +
    646 /**
    +
    647  * qlist->reverse(): Reverse the order of elements.
    +
    648  *
    +
    649  * @param list qlist_t container pointer.
    +
    650  */
    +
    651 void qlist_reverse(qlist_t *list) {
    +
    652  qlist_lock(list);
    +
    653  qlist_obj_t *obj;
    +
    654  for (obj = list->first; obj;) {
    +
    655  qlist_obj_t *next = obj->next;
    +
    656  obj->next = obj->prev;
    +
    657  obj->prev = next;
    +
    658  obj = next;
    +
    659  }
    +
    660 
    +
    661  obj = list->first;
    +
    662  list->first = list->last;
    +
    663  list->last = obj;
    +
    664 
    +
    665  qlist_unlock(list);
    +
    666 }
    +
    667 
    +
    668 /**
    +
    669  * qlist->clear(): Removes all of the elements from this list.
    +
    670  *
    +
    671  * @param list qlist_t container pointer.
    +
    672  */
    +
    673 void qlist_clear(qlist_t *list) {
    +
    674  qlist_lock(list);
    +
    675  qlist_obj_t *obj;
    +
    676  for (obj = list->first; obj;) {
    +
    677  qlist_obj_t *next = obj->next;
    +
    678  free(obj->data);
    +
    679  free(obj);
    +
    680  obj = next;
    +
    681  }
    +
    682 
    +
    683  list->num = 0;
    +
    684  list->datasum = 0;
    +
    685  list->first = NULL;
    +
    686  list->last = NULL;
    +
    687  qlist_unlock(list);
    +
    688 }
    +
    689 
    +
    690 /**
    +
    691  * qlist->toarray(): Returns the serialized chunk containing all the
    +
    692  * elements in this list.
    +
    693  *
    +
    694  * @param list qlist_t container pointer.
    +
    695  * @param size if size is not NULL, chunk size will be stored.
    +
    696  *
    +
    697  * @return a malloced pointer,
    +
    698  * otherwise(if there is no data to merge) returns NULL.
    +
    699  * @retval errno will be set in error condition.
    +
    700  * -ENOENT : List is empty.
    +
    701  * -ENOMEM : Memory allocation failure.
    +
    702  */
    +
    703 void *qlist_toarray(qlist_t *list, size_t *size) {
    +
    704  if (list->num <= 0) {
    +
    705  if (size != NULL)
    +
    706  *size = 0;
    +
    707  errno = ENOENT;
    +
    708  return NULL;
    +
    709  }
    +
    710 
    +
    711  qlist_lock(list);
    +
    712 
    +
    713  void *chunk = malloc(list->datasum);
    +
    714  if (chunk == NULL) {
    +
    715  qlist_unlock(list);
    +
    716  errno = ENOMEM;
    +
    717  return NULL;
    +
    718  }
    +
    719  void *dp = chunk;
    +
    720 
    +
    721  qlist_obj_t *obj;
    +
    722  for (obj = list->first; obj; obj = obj->next) {
    +
    723  memcpy(dp, obj->data, obj->size);
    +
    724  dp += obj->size;
    +
    725  }
    +
    726  qlist_unlock(list);
    +
    727 
    +
    728  if (size != NULL)
    +
    729  *size = list->datasum;
    +
    730  return chunk;
    +
    731 }
    +
    732 
    +
    733 /**
    +
    734  * qlist->tostring(): Returns a string representation of this list,
    +
    735  * containing string representation of each element.
    +
    736  *
    +
    737  * @param list qlist_t container pointer.
    +
    738  *
    +
    739  * @return a malloced pointer,
    +
    740  * otherwise(if there is no data to merge) returns NULL.
    +
    741  * @retval errno will be set in error condition.
    +
    742  * -ENOENT : List is empty.
    +
    743  * -ENOMEM : Memory allocation failure.
    +
    744  *
    +
    745  * @note
    +
    746  * Return string is always terminated by '\0'.
    +
    747  */
    +
    748 char *qlist_tostring(qlist_t *list) {
    +
    749  if (list->num <= 0) {
    +
    750  errno = ENOENT;
    +
    751  return NULL;
    +
    752  }
    +
    753 
    +
    754  qlist_lock(list);
    +
    755 
    +
    756  void *chunk = malloc(list->datasum + 1);
    +
    757  if (chunk == NULL) {
    +
    758  qlist_unlock(list);
    +
    759  errno = ENOMEM;
    +
    760  return NULL;
    +
    761  }
    +
    762  void *dp = chunk;
    +
    763 
    +
    764  qlist_obj_t *obj;
    +
    765  for (obj = list->first; obj; obj = obj->next) {
    +
    766  size_t size = obj->size;
    +
    767  // do not copy tailing '\0'
    +
    768  if (*(char *) (obj->data + (size - 1)) == '\0')
    +
    769  size -= 1;
    +
    770  memcpy(dp, obj->data, size);
    +
    771  dp += size;
    +
    772  }
    +
    773  *((char *) dp) = '\0';
    +
    774  qlist_unlock(list);
    +
    775 
    +
    776  return (char *) chunk;
    +
    777 }
    +
    778 
    +
    779 /**
    +
    780  * qlist->debug(): Prints out stored elements for debugging purpose.
    +
    781  *
    +
    782  * @param list qlist_t container pointer.
    +
    783  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    784  *
    +
    785  * @return true if successful, otherwise returns false.
    +
    786  * @retval errno will be set in error condition.
    +
    787  * -EIO : Invalid output stream.
    +
    788  */
    +
    789 bool qlist_debug(qlist_t *list, FILE *out) {
    +
    790  if (out == NULL) {
    +
    791  errno = EIO;
    +
    792  return false;
    +
    793  }
    +
    794 
    +
    795  qlist_lock(list);
    +
    796  qlist_obj_t *obj;
    +
    797  int i;
    +
    798  for (i = 0, obj = list->first; obj; obj = obj->next, i++) {
    +
    799  fprintf(out, "%d=", i);
    +
    800  _q_textout(out, obj->data, obj->size, MAX_HUMANOUT);
    +
    801  fprintf(out, " (%zu)\n", obj->size);
    +
    802  }
    +
    803  qlist_unlock(list);
    +
    804 
    +
    805  return true;
    +
    806 }
    +
    807 
    +
    808 /**
    +
    809  * qlist->lock(): Enters critical section.
    +
    810  *
    +
    811  * @param list qlist_t container pointer.
    +
    812  *
    +
    813  * @note
    +
    814  * From user side, normally locking operation is only needed when traverse all
    +
    815  * elements using qlist->getnext().
    +
    816  */
    +
    817 void qlist_lock(qlist_t *list) {
    +
    818  Q_MUTEX_ENTER(list->qmutex);
    +
    819 }
    +
    820 
    +
    821 /**
    +
    822  * qlist->unlock(): Leaves critical section.
    +
    823  *
    +
    824  * @param list qlist_t container pointer.
    +
    825  */
    +
    826 void qlist_unlock(qlist_t *list) {
    +
    827  Q_MUTEX_LEAVE(list->qmutex);
    +
    828 }
    +
    829 
    +
    830 /**
    +
    831  * qlist->free(): Free qlist_t.
    +
    832  *
    +
    833  * @param list qlist_t container pointer.
    +
    834  */
    +
    835 void qlist_free(qlist_t *list) {
    +
    836  qlist_clear(list);
    +
    837  Q_MUTEX_DESTROY(list->qmutex);
    +
    838 
    +
    839  free(list);
    +
    840 }
    +
    841 
    +
    842 #ifndef _DOXYGEN_SKIP
    +
    843 
    +
    844 static void *get_at(qlist_t *list, int index, size_t *size, bool newmem,
    +
    845 bool remove) {
    +
    846  qlist_lock(list);
    +
    847 
    +
    848  // get object pointer
    +
    849  qlist_obj_t *obj = get_obj(list, index);
    +
    850  if (obj == NULL) {
    +
    851  qlist_unlock(list);
    +
    852  return false;
    +
    853  }
    +
    854 
    +
    855  // copy data
    +
    856  void *data;
    +
    857  if (newmem == true) {
    +
    858  data = malloc(obj->size);
    +
    859  if (data == NULL) {
    +
    860  qlist_unlock(list);
    +
    861  errno = ENOMEM;
    +
    862  return false;
    +
    863  }
    +
    864  memcpy(data, obj->data, obj->size);
    +
    865  } else {
    +
    866  data = obj->data;
    +
    867  }
    +
    868  if (size != NULL)
    +
    869  *size = obj->size;
    +
    870 
    +
    871  // remove if necessary
    +
    872  if (remove == true) {
    +
    873  if (remove_obj(list, obj) == false) {
    +
    874  if (newmem == true)
    +
    875  free(data);
    +
    876  data = NULL;
    +
    877  }
    +
    878  }
    +
    879 
    +
    880  qlist_unlock(list);
    +
    881 
    +
    882  return data;
    +
    883 }
    +
    884 
    +
    885 static qlist_obj_t *get_obj(qlist_t *list, int index) {
    +
    886  // index adjustment
    +
    887  if (index < 0)
    +
    888  index = list->num + index;
    +
    889  if (index >= list->num) {
    +
    890  errno = ERANGE;
    +
    891  return NULL;
    +
    892  }
    +
    893 
    +
    894  // detect faster scan direction
    +
    895  bool backward;
    +
    896  qlist_obj_t *obj;
    +
    897  int listidx;
    +
    898  if (index < list->num / 2) {
    +
    899  backward = false;
    +
    900  obj = list->first;
    +
    901  listidx = 0;
    +
    902  } else {
    +
    903  backward = true;
    +
    904  obj = list->last;
    +
    905  listidx = list->num - 1;
    +
    906  }
    +
    907 
    +
    908  // find object
    +
    909  while (obj != NULL) {
    +
    910  if (listidx == index)
    +
    911  return obj;
    +
    912 
    +
    913  if (backward == false) {
    +
    914  obj = obj->next;
    +
    915  listidx++;
    +
    916  } else {
    +
    917  obj = obj->prev;
    +
    918  listidx--;
    +
    919  }
    +
    920  }
    +
    921 
    +
    922  // never reach here
    +
    923  errno = ENOENT;
    +
    924  return NULL;
    +
    925 }
    +
    926 
    +
    927 static bool remove_obj(qlist_t *list, qlist_obj_t *obj) {
    +
    928  if (obj == NULL)
    +
    929  return false;
    +
    930 
    +
    931  // chain prev and next elements
    +
    932  if (obj->prev == NULL)
    +
    933  list->first = obj->next;
    +
    934  else
    +
    935  obj->prev->next = obj->next;
    +
    936  if (obj->next == NULL)
    +
    937  list->last = obj->prev;
    +
    938  else
    +
    939  obj->next->prev = obj->prev;
    +
    940 
    +
    941  // adjust counter
    +
    942  list->datasum -= obj->size;
    +
    943  list->num--;
    +
    944 
    +
    945  // release obj
    +
    946  free(obj->data);
    +
    947  free(obj);
    +
    948 
    +
    949  return true;
    +
    950 }
    +
    951 
    +
    952 #endif /* _DOXYGEN_SKIP */
    +
    void * qlist_popat(qlist_t *list, int index, size_t *size)
    qlist->popat(): Returns and remove the element at the specified position in this list.
    Definition: qlist.c:495
    +
    bool qlist_getnext(qlist_t *list, qlist_obj_t *obj, bool newmem)
    qlist->getnext(): Get next element in this list.
    Definition: qlist.c:583
    +
    void qlist_unlock(qlist_t *list)
    qlist->unlock(): Leaves critical section.
    Definition: qlist.c:826
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124
    +
    void qlist_reverse(qlist_t *list)
    qlist->reverse(): Reverse the order of elements.
    Definition: qlist.c:651
    +
    void * qlist_poplast(qlist_t *list, size_t *size)
    qlist->getlast(): Returns and remove the last element in this list.
    Definition: qlist.c:466
    +
    bool qlist_removelast(qlist_t *list)
    qlist->removelast(): Removes the last element in this list.
    Definition: qlist.c:521
    +
    bool qlist_addat(qlist_t *list, int index, const void *data, size_t size)
    qlist->addat(): Inserts a element at the specified position in this list.
    Definition: qlist.c:276
    +
    bool qlist_addfirst(qlist_t *list, const void *data, size_t size)
    qlist->addfirst(): Inserts a element at the beginning of this list.
    Definition: qlist.c:221
    +
    void qlist_free(qlist_t *list)
    qlist->free(): Free qlist_t.
    Definition: qlist.c:835
    +
    bool qlist_debug(qlist_t *list, FILE *out)
    qlist->debug(): Prints out stored elements for debugging purpose.
    Definition: qlist.c:789
    +
    char * qlist_tostring(qlist_t *list)
    qlist->tostring(): Returns a string representation of this list, containing string representation of ...
    Definition: qlist.c:748
    +
    bool qlist_addlast(qlist_t *list, const void *data, size_t size)
    qlist->addlast(): Appends a element to the end of this list.
    Definition: qlist.c:239
    +
    size_t qlist_datasize(qlist_t *list)
    qlist->size(): Returns the sum of total element size.
    Definition: qlist.c:642
    +
    bool qlist_removefirst(qlist_t *list)
    qlist->removefirst(): Removes the first element in this list.
    Definition: qlist.c:508
    +
    size_t qlist_size(qlist_t *list)
    qlist->size(): Returns the number of elements in this list.
    Definition: qlist.c:631
    +
    void * qlist_getlast(qlist_t *list, size_t *size, bool newmem)
    qlist->getlast(): Returns the last element in this list.
    Definition: qlist.c:405
    +
    void qlist_clear(qlist_t *list)
    qlist->clear(): Removes all of the elements from this list.
    Definition: qlist.c:673
    +
    void qlist_lock(qlist_t *list)
    qlist->lock(): Enters critical section.
    Definition: qlist.c:817
    +
    void * qlist_popfirst(qlist_t *list, size_t *size)
    qlist->popfirst(): Returns and remove the first element in this list.
    Definition: qlist.c:451
    +
    void * qlist_toarray(qlist_t *list, size_t *size)
    qlist->toarray(): Returns the serialized chunk containing all the elements in this list.
    Definition: qlist.c:703
    +
    size_t qlist_setsize(qlist_t *list, size_t max)
    qlist->setsize(): Limit maximum number of elements allowed in this list.
    Definition: qlist.c:190
    +
    void * qlist_getat(qlist_t *list, int index, size_t *size, bool newmem)
    qlist->getat(): Returns the element at the specified position in this list.
    Definition: qlist.c:436
    +
    void * qlist_getfirst(qlist_t *list, size_t *size, bool newmem)
    qlist->getfirst(): Returns the first element in this list.
    Definition: qlist.c:389
    +
    bool qlist_removeat(qlist_t *list, int index)
    qlist->removeat(): Removes the element at the specified position in this list.
    Definition: qlist.c:536
    diff --git a/doc/html/qlisttbl_8c.html b/doc/html/qlisttbl_8c.html index f10c9499..20958eb6 100644 --- a/doc/html/qlisttbl_8c.html +++ b/doc/html/qlisttbl_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qlisttbl.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@

    @@ -61,7 +60,8 @@
    -
    qlisttbl.c File Reference
    +
    +
    qlisttbl.c File Reference
    @@ -70,73 +70,73 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - - - - - - + + + + + + - + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - +

    +

    Functions

    qlisttbl_t * qlisttbl (int options)
     Create a new Q_LIST linked-list container.
     
    qlisttbl_t * qlisttbl (int options)
     Create a new Q_LIST linked-list container. More...
     
    bool qlisttbl_put (qlisttbl_t *tbl, const char *name, const void *data, size_t size)
     qlisttbl->put(): Put an element to this table.
     qlisttbl->put(): Put an element to this table. More...
     
    bool qlisttbl_putstr (qlisttbl_t *tbl, const char *name, const char *str)
     qlisttbl->putstr(): Put a string into this table.
     qlisttbl->putstr(): Put a string into this table. More...
     
    bool qlisttbl_putstrf (qlisttbl_t *tbl, const char *name, const char *format,...)
     qlisttbl->putstrf(): Put a formatted string into this table.
     qlisttbl->putstrf(): Put a formatted string into this table. More...
     
    bool qlisttbl_putint (qlisttbl_t *tbl, const char *name, int64_t num)
     qlisttbl->putInt(): Put an integer into this table as string type.
     qlisttbl->putInt(): Put an integer into this table as string type. More...
     
    void * qlisttbl_get (qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
     qlisttbl->get(): Finds an object with given name.
     
    char * qlisttbl_getstr (qlisttbl_t *tbl, const char *name, bool newmem)
     qlisttbl->getstr(): Finds an object with given name and returns as string type.
     
    void * qlisttbl_get (qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
     qlisttbl->get(): Finds an object with given name. More...
     
    char * qlisttbl_getstr (qlisttbl_t *tbl, const char *name, bool newmem)
     qlisttbl->getstr(): Finds an object with given name and returns as string type. More...
     
    int64_t qlisttbl_getint (qlisttbl_t *tbl, const char *name)
     qlisttbl->getint(): Finds an object with given name and returns as integer type.
     qlisttbl->getint(): Finds an object with given name and returns as integer type. More...
     
    qlisttbl_data_t * qlisttbl_getmulti (qlisttbl_t *tbl, const char *name, bool newmem, size_t *numobjs)
     qlisttbl->getmulti(): Finds all objects with given name and return a array of objects.
     
    qlisttbl_data_t * qlisttbl_getmulti (qlisttbl_t *tbl, const char *name, bool newmem, size_t *numobjs)
     qlisttbl->getmulti(): Finds all objects with given name and return a array of objects. More...
     
    void qlisttbl_freemulti (qlisttbl_data_t *objs)
     qlisttbl->freemulti(): Deallocate object array returned by getmulti().
     qlisttbl->freemulti(): Deallocate object array returned by getmulti(). More...
     
    size_t qlisttbl_remove (qlisttbl_t *tbl, const char *name)
     qlisttbl->remove(): Remove matched objects with given name.
     qlisttbl->remove(): Remove matched objects with given name. More...
     
    bool qlisttbl_removeobj (qlisttbl_t *tbl, const qlisttbl_obj_t *obj)
     qlisttbl->removeobj(): Remove objects with given object pointer.
     qlisttbl->removeobj(): Remove objects with given object pointer. More...
     
    bool qlisttbl_getnext (qlisttbl_t *tbl, qlisttbl_obj_t *obj, const char *name, bool newmem)
     qlisttbl->getnext(): Get next element.
     qlisttbl->getnext(): Get next element. More...
     
    size_t qlisttbl_size (qlisttbl_t *tbl)
     qlisttbl->size(): Returns the number of elements in this table.
     qlisttbl->size(): Returns the number of elements in this table. More...
     
    void qlisttbl_sort (qlisttbl_t *tbl)
     qlisttbl->sort(): Sort keys in this table.
     qlisttbl->sort(): Sort keys in this table. More...
     
    void qlisttbl_clear (qlisttbl_t *tbl)
     qlisttbl->clear(): Removes all of the elements from this table.
     qlisttbl->clear(): Removes all of the elements from this table. More...
     
    bool qlisttbl_save (qlisttbl_t *tbl, const char *filepath, char sepchar, bool encode)
     qlisttbl->save(): Save qlisttbl as plain text format Dumping direction is always forward(from the top to the bottom) to preserve the order when we load the file again.
     qlisttbl->save(): Save qlisttbl as plain text format Dumping direction is always forward(from the top to the bottom) to preserve the order when we load the file again. More...
     
    ssize_t qlisttbl_load (qlisttbl_t *tbl, const char *filepath, char sepchar, bool decode)
     qlisttbl->load(): Load and append entries from given filepath Loading direction is always appending at the bottom of the table to preserve the order as it was.
     qlisttbl->load(): Load and append entries from given filepath Loading direction is always appending at the bottom of the table to preserve the order as it was. More...
     
    bool qlisttbl_debug (qlisttbl_t *tbl, FILE *out)
     qlisttbl->debug(): Print out stored elements for debugging purpose.
     qlisttbl->debug(): Print out stored elements for debugging purpose. More...
     
    void qlisttbl_lock (qlisttbl_t *tbl)
     qlisttbl->lock(): Enter critical section.
     qlisttbl->lock(): Enter critical section. More...
     
    void qlisttbl_unlock (qlisttbl_t *tbl)
     qlisttbl->unlock(): Leave critical section.
     qlisttbl->unlock(): Leave critical section. More...
     
    void qlisttbl_free (qlisttbl_t *tbl)
     qlisttbl->free(): Free qlisttbl_t
     qlisttbl->free(): Free qlisttbl_t More...
     

    Detailed Description

    @@ -158,7 +158,7 @@
    | KEY B | | VALUE B |
    +-------+ +---------+
    // create a list table.
    -
    qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    +
    qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    // insert elements (key duplication allowed)
    tbl->put(tbl, "e1", "a", strlen("e1")+1); // equal to putstr();
    @@ -191,18 +191,18 @@
    // free object
    tbl->free(tbl);
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150

    Definition in file qlisttbl.c.

    Function Documentation

    - -

    ◆ qlisttbl()

    + +

    ◆ qlisttbl()

    - + @@ -228,8 +228,8 @@

    -
    qlisttbl_t *threadsafe_tbl = qlisttbl(QLISTTBL_THREADSAFE);
    +
    qlisttbl_t *tbl = qlisttbl(0);
    +
    qlisttbl_t *threadsafe_tbl = qlisttbl(QLISTTBL_THREADSAFE);
    Note
    Available options:
    • QLISTTBL_THREADSAFE - make it thread-safe.
    • QLISTTBL_UNIQUE - keys are all unique. replace same key
    • @@ -243,8 +243,8 @@

      -

      ◆ qlisttbl_put()

      + +

      ◆ qlisttbl_put()

      @@ -310,7 +310,7 @@

      struct my_obj obj;

      // create a table and add a sample object.
      -
      +
      qlisttbl_t *tbl = qlisttbl();
      tbl->put(tbl, "obj1", &obj, sizeof(struct my_obj));
      Note
      The default behavior is adding an object at the end of this table unless QLISTTBL_INSERTTOP option was given.
      @@ -318,8 +318,8 @@

      -

      ◆ qlisttbl_putstr()

      + +

      ◆ qlisttbl_putstr()

      @@ -375,8 +375,8 @@

      -

      ◆ qlisttbl_putstrf()

      + +

      ◆ qlisttbl_putstrf()

      @@ -438,8 +438,8 @@

      -

      ◆ qlisttbl_putint()

      + +

      ◆ qlisttbl_putint()

      @@ -496,14 +496,14 @@

      -

      ◆ qlisttbl_get()

      + +

      ◆ qlisttbl_get()

    qlisttbl_t * qlisttbl qlisttbl_t* qlisttbl ( int  options)
    - + @@ -557,7 +557,7 @@

    +
    qlisttbl_t *tbl = qlisttbl();
    (...codes...)
    // with newmem flag unset
    @@ -575,14 +575,14 @@

    -

    ◆ qlisttbl_getstr()

    + +

    ◆ qlisttbl_getstr()

    void * qlisttbl_get void* qlisttbl_get ( qlisttbl_t *  tbl,
    - + @@ -633,8 +633,8 @@

    -

    ◆ qlisttbl_getint()

    + +

    ◆ qlisttbl_getint()

    @@ -684,14 +684,14 @@

    -

    ◆ qlisttbl_getmulti()

    + +

    ◆ qlisttbl_getmulti()

    char * qlisttbl_getstr char* qlisttbl_getstr ( qlisttbl_t *  tbl,
    - + @@ -757,8 +757,8 @@

    -

    ◆ qlisttbl_freemulti()

    + +

    ◆ qlisttbl_freemulti()

    @@ -790,8 +790,8 @@

    -

    ◆ qlisttbl_remove()

    + +

    ◆ qlisttbl_remove()

    @@ -830,8 +830,8 @@

    -

    ◆ qlisttbl_removeobj()

    + +

    ◆ qlisttbl_removeobj()

    @@ -887,8 +887,8 @@

    -

    ◆ qlisttbl_getnext()

    + +

    ◆ qlisttbl_getnext()

    @@ -948,7 +948,7 @@

    Note
    The obj should be initialized with 0 by using memset() before first call. If newmem flag is true, user should de-allocate obj.name and obj.data resources.
    -
    +
    qlisttbl_t *tbl = qlisttbl();
    (...add data into table...)
    // non-thread usages
    @@ -975,8 +975,8 @@

    -

    ◆ qlisttbl_size()

    + +

    ◆ qlisttbl_size()

    @@ -1004,8 +1004,8 @@

    -

    ◆ qlisttbl_sort()

    + +

    ◆ qlisttbl_sort()

    @@ -1043,8 +1043,8 @@

    -

    ◆ qlisttbl_clear()

    + +

    ◆ qlisttbl_clear()

    @@ -1071,8 +1071,8 @@

    -

    ◆ qlisttbl_save()

    + +

    ◆ qlisttbl_save()

    @@ -1125,8 +1125,8 @@

    -

    ◆ qlisttbl_load()

    + +

    ◆ qlisttbl_load()

    @@ -1179,8 +1179,8 @@

    -

    ◆ qlisttbl_debug()

    + +

    ◆ qlisttbl_debug()

    @@ -1228,8 +1228,8 @@

    -

    ◆ qlisttbl_lock()

    + +

    ◆ qlisttbl_lock()

    @@ -1257,8 +1257,8 @@

    -

    ◆ qlisttbl_unlock()

    + +

    ◆ qlisttbl_unlock()

    @@ -1285,8 +1285,8 @@

    -

    ◆ qlisttbl_free()

    + +

    ◆ qlisttbl_free()

    @@ -1319,7 +1319,7 @@

    diff --git a/doc/html/qlisttbl_8c.js b/doc/html/qlisttbl_8c.js index 7720a3cd..0290d4fb 100644 --- a/doc/html/qlisttbl_8c.js +++ b/doc/html/qlisttbl_8c.js @@ -1,14 +1,14 @@ var qlisttbl_8c = [ - [ "qlisttbl", "qlisttbl_8c.html#a0df1c9358a8712794a8a0030a1687f62", null ], + [ "qlisttbl", "qlisttbl_8c.html#ace8778acce088344531b5249d580aca7", null ], [ "qlisttbl_put", "qlisttbl_8c.html#a073065190797d183a08f0a5325a01a1e", null ], [ "qlisttbl_putstr", "qlisttbl_8c.html#a7b4fe528a8b64efd31e7927fbcfcc7ab", null ], [ "qlisttbl_putstrf", "qlisttbl_8c.html#a10ea86e362efe15c888a91444d48af73", null ], [ "qlisttbl_putint", "qlisttbl_8c.html#a2bc37eee74430649a238b7c3dd62518b", null ], - [ "qlisttbl_get", "qlisttbl_8c.html#ad5d3f27fe3d3e32327463dc86c73283e", null ], - [ "qlisttbl_getstr", "qlisttbl_8c.html#a759de18f2f11bfb03a7d07a1c06f3152", null ], + [ "qlisttbl_get", "qlisttbl_8c.html#afb58625be8850adff352adeaccf3be83", null ], + [ "qlisttbl_getstr", "qlisttbl_8c.html#a7990202b09ab853c07221458755278b6", null ], [ "qlisttbl_getint", "qlisttbl_8c.html#af14aa8d272fa39b24260ead37410e4ac", null ], - [ "qlisttbl_getmulti", "qlisttbl_8c.html#a060ae322424932b3cc2fda83a037e1e2", null ], + [ "qlisttbl_getmulti", "qlisttbl_8c.html#af5c61d4dd8b45def720b4e96708da7f2", null ], [ "qlisttbl_freemulti", "qlisttbl_8c.html#a3e0d4e684118fd2b6f65b0a71c2306b4", null ], [ "qlisttbl_remove", "qlisttbl_8c.html#ac494b02523019a449c29cf1049ef4e60", null ], [ "qlisttbl_removeobj", "qlisttbl_8c.html#a41e4658b9680da54c0d7b97ae046999c", null ], diff --git a/doc/html/qlisttbl_8c_source.html b/doc/html/qlisttbl_8c_source.html index 7720e1e4..1b443c42 100644 --- a/doc/html/qlisttbl_8c_source.html +++ b/doc/html/qlisttbl_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qlisttbl.c Source File @@ -20,8 +20,8 @@

    qlisttbl_data_t * qlisttbl_getmulti qlisttbl_data_t* qlisttbl_getmulti ( qlisttbl_t *  tbl,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1209 +52,1210 @@

    -
    qlisttbl.c
    +
    +
    qlisttbl.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qlisttbl.c Linked-list-table implementation.
    -
    31 *
    -
    32 * qlisttbl container is a Linked-List-Table implementation.
    -
    33 * Which maps keys to values. Key is a string and value is any non-null object.
    -
    34 * These elements are stored sequentially in Doubly-Linked-List data structure.
    -
    35 *
    -
    36 * Compared to Hash-Table, List-Table is efficient when you need to keep
    -
    37 * duplicated keys since Hash-Table only keep unique keys. Of course, qlisttbl
    -
    38 * supports both unique keys and key duplication.
    -
    39 *
    -
    40 * @code
    -
    41 * [Conceptional Data Structure Diagram]
    -
    42 *
    -
    43 * last~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
    -
    44 * |
    -
    45 * +-----------+ doubly +-----------+ doubly +-v---------+
    -
    46 * first~~~|~> 0 <~|~~~~~~~~~~|~> 1 <~|~~~~~~~~~~|~> N |
    -
    47 * +--|-----|--+ linked +--|-----|--+ linked +--|-----|--+
    -
    48 * | | | | | |
    -
    49 * +-----v-+ +-v-------+ | | +-----v-+ +-v-------+
    -
    50 * | KEY A | | VALUE A | | | | KEY N | | VALUE N |
    -
    51 * +-------+ +---------+ | | +-------+ +---------+
    -
    52 * +-----v-+ +-v-------+
    -
    53 * | KEY B | | VALUE B |
    -
    54 * +-------+ +---------+
    -
    55 * @endcode
    -
    56 *
    -
    57 * @code
    -
    58 * // create a list table.
    -
    59 * qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    -
    60 *
    -
    61 * // insert elements (key duplication allowed)
    -
    62 * tbl->put(tbl, "e1", "a", strlen("e1")+1); // equal to putstr();
    -
    63 * tbl->putstr(tbl, "e2", "b");
    -
    64 * tbl->putstr(tbl, "e2", "c");
    -
    65 * tbl->putstr(tbl, "e3", "d");
    -
    66 *
    -
    67 * // debug output
    -
    68 * tbl->debug(tbl, stdout, true);
    -
    69 *
    -
    70 * // get element
    -
    71 * printf("get('e2') : %s\n", (char*)tbl->get(tbl, "e2", NULL, false));
    -
    72 * printf("getstr('e2') : %s\n", tbl->getstr(tbl, "e2", false));
    -
    73 *
    -
    74 * // get all matching elements
    -
    75 * qlisttbl_data_t *objs = tbl->getmulti(tbl, "e2", true, NULL);
    -
    76 * for (i = 0; objs[i].data != NULL; i++) {
    -
    77 * printf("getmulti('e2')[%d] : %s (size=%d)\n",
    -
    78 * i, (char *)objs[i].data, (int)objs[i].size);
    -
    79 * }
    -
    80 * tbl->freemulti(objs);
    -
    81 *
    -
    82 * // find every 'e2' elements
    -
    83 * qlisttbl_obj_t obj;
    -
    84 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    85 * while(tbl->getnext(tbl, &obj, "e2", false) == true) {
    -
    86 * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    -
    87 * obj.name, (char*)obj.data, obj.size);
    -
    88 * }
    -
    89 *
    -
    90 * // free object
    -
    91 * tbl->free(tbl);
    -
    92 * @endcode
    -
    93 */
    -
    94
    -
    95#include <stdio.h>
    -
    96#include <stdlib.h>
    -
    97#include <stdbool.h>
    -
    98#include <stdint.h>
    -
    99#include <inttypes.h>
    -
    100#include <string.h>
    -
    101#include <stdarg.h>
    -
    102#include <unistd.h>
    -
    103#include <fcntl.h>
    -
    104#include <sys/stat.h>
    -
    105#include <assert.h>
    -
    106#include <errno.h>
    -
    107#include "qinternal.h"
    -
    108#include "utilities/qencode.h"
    -
    109#include "utilities/qfile.h"
    -
    110#include "utilities/qhash.h"
    -
    111#include "utilities/qio.h"
    -
    112#include "utilities/qstring.h"
    -
    113#include "utilities/qtime.h"
    -
    114#include "containers/qlisttbl.h"
    -
    115
    -
    116#ifndef _DOXYGEN_SKIP
    -
    117
    -
    118static qlisttbl_obj_t *newobj(const char *name, const void *data, size_t size);
    -
    119static bool insertobj(qlisttbl_t *tbl, qlisttbl_obj_t *obj);
    -
    120static qlisttbl_obj_t *findobj(qlisttbl_t *tbl, const char *name, qlisttbl_obj_t *retobj);
    -
    121
    -
    122static bool namematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash);
    -
    123static bool namecasematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash);
    -
    124
    -
    125#endif
    -
    126
    -
    127/**
    -
    128 * Create a new Q_LIST linked-list container
    -
    129 *
    -
    130 * @param options combination of initialization options.
    -
    131 *
    -
    132 * @return a pointer of malloced qlisttbl_t structure in case of successful,
    -
    133 * otherwise returns NULL.
    -
    134 * @retval errno will be set in error condition.
    -
    135 * - ENOMEM : Memory allocation failure.
    -
    136 *
    -
    137 * @code
    -
    138 * qlisttbl_t *tbl = qlisttbl(0);
    -
    139 * qlisttbl_t *threadsafe_tbl = qlisttbl(QLISTTBL_THREADSAFE);
    -
    140 * @endcode
    -
    141 *
    -
    142 * @note
    -
    143 * Available options:
    -
    144 * - QLISTTBL_THREADSAFE - make it thread-safe.
    -
    145 * - QLISTTBL_UNIQUE - keys are all unique. replace same key
    -
    146 * - QLISTTBL_CASEINSENSITIVE - key is case insensitive
    -
    147 * - QLISTTBL_INSERTTOP - insert new key at the top
    -
    148 * - QLISTTBL_LOOKUPFORWARD - find key from the top
    -
    149 */
    -
    150qlisttbl_t *qlisttbl(int options)
    -
    151{
    -
    152 qlisttbl_t *tbl = (qlisttbl_t *)calloc(1, sizeof(qlisttbl_t));
    -
    153 if (tbl == NULL) {
    -
    154 errno = ENOMEM;
    -
    155 return NULL;
    -
    156 }
    -
    157
    -
    158 // assign member methods.
    -
    159 tbl->put = qlisttbl_put;
    -
    160 tbl->putstr = qlisttbl_putstr;
    -
    161 tbl->putstrf = qlisttbl_putstrf;
    -
    162 tbl->putint = qlisttbl_putint;
    -
    163
    -
    164 tbl->get = qlisttbl_get;
    -
    165 tbl->getstr = qlisttbl_getstr;
    -
    166 tbl->getint = qlisttbl_getint;
    -
    167
    -
    168 tbl->getmulti = qlisttbl_getmulti;
    -
    169 tbl->freemulti = qlisttbl_freemulti;
    -
    170
    -
    171 tbl->remove = qlisttbl_remove;
    -
    172 tbl->removeobj = qlisttbl_removeobj;
    -
    173
    -
    174 tbl->getnext = qlisttbl_getnext;
    -
    175
    -
    176 tbl->size = qlisttbl_size;
    -
    177 tbl->sort = qlisttbl_sort;
    -
    178 tbl->clear = qlisttbl_clear;
    -
    179 tbl->save = qlisttbl_save;
    -
    180 tbl->load = qlisttbl_load;
    -
    181
    -
    182 tbl->debug = qlisttbl_debug;
    -
    183
    -
    184 tbl->lock = qlisttbl_lock;
    -
    185 tbl->unlock = qlisttbl_unlock;
    -
    186
    -
    187 tbl->free = qlisttbl_free;
    -
    188
    -
    189 // assign private methods.
    -
    190 tbl->namematch = namematch;
    -
    191 tbl->namecmp = strcmp;
    -
    192
    -
    193 // handle options.
    -
    194 if (options & QLISTTBL_THREADSAFE) {
    -
    195 Q_MUTEX_NEW(tbl->qmutex, true);
    -
    196 if (tbl->qmutex == NULL) {
    -
    197 errno = ENOMEM;
    -
    198 free(tbl);
    -
    199 return NULL;
    -
    200 }
    -
    201 }
    -
    202 if (options & QLISTTBL_UNIQUE) {
    -
    203 tbl->unique = true;
    -
    204 }
    -
    205 if (options & QLISTTBL_CASEINSENSITIVE) {
    -
    206 tbl->namematch = namecasematch;
    -
    207 tbl->namecmp = strcasecmp;
    -
    208 }
    -
    209 if (options & QLISTTBL_INSERTTOP) {
    -
    210 tbl->inserttop = true;
    -
    211 }
    -
    212 if (options & QLISTTBL_LOOKUPFORWARD) {
    -
    213 tbl->lookupforward = true;
    -
    214 }
    -
    215
    -
    216 return tbl;
    -
    217}
    -
    218
    -
    219/**
    -
    220 * qlisttbl->put(): Put an element to this table.
    -
    221 *
    -
    222 * @param tbl qlisttbl container pointer.
    -
    223 * @param name element name.
    -
    224 * @param data a pointer which points data memory.
    -
    225 * @param size size of the data.
    -
    226 *
    -
    227 * @return true if successful, otherwise returns false.
    -
    228 * @retval errno will be set in error condition.
    -
    229 * - ENOMEM : Memory allocation failure.
    -
    230 * - EINVAL : Invalid argument.
    -
    231 *
    -
    232 * @code
    -
    233 * struct my_obj {
    -
    234 * int a, b, c;
    -
    235 * };
    -
    236 *
    -
    237 * // create a sample object.
    -
    238 * struct my_obj obj;
    -
    239 *
    -
    240 * // create a table and add a sample object.
    -
    241 * qlisttbl_t *tbl = qlisttbl();
    -
    242 * tbl->put(tbl, "obj1", &obj, sizeof(struct my_obj));
    -
    243 * @endcode
    -
    244 *
    -
    245 * @note
    -
    246 * The default behavior is adding an object at the end of this table
    -
    247 * unless QLISTTBL_INSERTTOP option was given.
    -
    248 */
    -
    249bool qlisttbl_put(qlisttbl_t *tbl, const char *name, const void *data, size_t size)
    -
    250{
    -
    251 // make new object table
    -
    252 qlisttbl_obj_t *obj = newobj(name, data, size);
    -
    253 if (obj == NULL) {
    -
    254 return false;
    -
    255 }
    -
    256
    -
    257 // lock table
    -
    258 qlisttbl_lock(tbl);
    -
    259
    -
    260 // if unique flag is set, remove same key
    -
    261 if (tbl->unique == true) qlisttbl_remove(tbl, name);
    -
    262
    -
    263 // insert into table
    -
    264 if (tbl->num == 0) {
    -
    265 obj->prev = NULL;
    -
    266 obj->next = NULL;
    -
    267 } else {
    -
    268 if (tbl->inserttop == false) {
    -
    269 obj->prev = tbl->last;
    -
    270 obj->next = NULL;
    -
    271 } else {
    -
    272 obj->prev = NULL;
    -
    273 obj->next = tbl->first;
    -
    274 }
    -
    275 }
    -
    276 insertobj(tbl, obj);
    -
    277
    -
    278 // unlock table
    -
    279 qlisttbl_unlock(tbl);
    -
    280
    -
    281 return true;
    -
    282}
    -
    283
    -
    284/**
    -
    285 * qlisttbl->putstr(): Put a string into this table.
    -
    286 *
    -
    287 * @param tbl qlisttbl container pointer.
    -
    288 * @param name element name.
    -
    289 * @param str string data.
    -
    290 *
    -
    291 * @return true if successful, otherwise returns false.
    -
    292 * @retval errno will be set in error condition.
    -
    293 * - ENOMEM : Memory allocation failure.
    -
    294 * - EINVAL : Invalid argument.
    -
    295 */
    -
    296bool qlisttbl_putstr(qlisttbl_t *tbl, const char *name, const char *str)
    -
    297{
    -
    298 size_t size = (str) ? (strlen(str) + 1) : 0;
    -
    299 return qlisttbl_put(tbl, name, (const void *)str, size);
    -
    300}
    -
    301
    -
    302/**
    -
    303 * qlisttbl->putstrf(): Put a formatted string into this table.
    -
    304 *
    -
    305 * @param tbl qlisttbl container pointer.
    -
    306 * @param name element name.
    -
    307 * @param format formatted value string.
    -
    308 *
    -
    309 * @return true if successful, otherwise returns false.
    -
    310 * @retval errno will be set in error condition.
    -
    311 * - ENOMEM : Memory allocation failure.
    -
    312 * - EINVAL : Invalid argument.
    -
    313 */
    -
    314bool qlisttbl_putstrf(qlisttbl_t *tbl, const char *name, const char *format, ...)
    -
    315{
    -
    316 char *str;
    -
    317 DYNAMIC_VSPRINTF(str, format);
    -
    318 if (str == NULL) {
    -
    319 errno = ENOMEM;
    -
    320 return false;
    -
    321 }
    -
    322
    -
    323 bool ret = qlisttbl_putstr(tbl, name, str);
    -
    324 free(str);
    -
    325
    -
    326 return ret;
    -
    327}
    -
    328
    -
    329/**
    -
    330 * qlisttbl->putInt(): Put an integer into this table as string type.
    -
    331 *
    -
    332 * @param tbl qlisttbl container pointer.
    -
    333 * @param name element name.
    -
    334 * @param num number data.
    -
    335 *
    -
    336 * @return true if successful, otherwise returns false.
    -
    337 * @retval errno will be set in error condition.
    -
    338 * - ENOMEM : Memory allocation failure.
    -
    339 * - EINVAL : Invalid argument.
    -
    340 *
    -
    341 * @note
    -
    342 * The integer will be converted to a string object and stored as a string
    -
    343 * object.
    -
    344 */
    -
    345bool qlisttbl_putint(qlisttbl_t *tbl, const char *name, int64_t num)
    -
    346{
    -
    347 char str[20+1];
    -
    348 snprintf(str, sizeof(str), "%"PRId64, num);
    -
    349 return qlisttbl_putstr(tbl, name, str);
    -
    350}
    -
    351
    -
    352/**
    -
    353 * qlisttbl->get(): Finds an object with given name.
    -
    354 *
    -
    355 * If there are duplicate keys in the table, this will return the first matched
    -
    356 * one from the bottom (or the top if QLISTTBL_LOOKUPFORWARD option was given).
    -
    357 * So if there are duplicated keys, it'll return recently inserted one.
    -
    358 *
    -
    359 * @param tbl qlisttbl container pointer.
    -
    360 * @param name element name.
    -
    361 * @param size if size is not NULL, data size will be stored.
    -
    362 * @param newmem whether or not to allocate memory for the data.
    -
    363 *
    -
    364 * @return a pointer of data if key is found, otherwise returns NULL.
    -
    365 * @retval errno will be set in error condition.
    -
    366 * - ENOENT : No such key found.
    -
    367 * - EINVAL : Invalid argument.
    -
    368 * - ENOMEM : Memory allocation failure.
    -
    369 *
    -
    370 * @code
    -
    371 * qlisttbl_t *tbl = qlisttbl();
    -
    372 * (...codes...)
    -
    373 *
    -
    374 * // with newmem flag unset
    -
    375 * size_t size;
    -
    376 * void *data = tbl->get(tbl, "key_name", &size, false);
    -
    377 *
    -
    378 * // with newmem flag set
    -
    379 * size_t size;
    -
    380 * void *data = tbl->get(tbl, "key_name", &size, true);
    -
    381 * (...does something with data...)
    -
    382 * if(data != NULL) free(data);
    -
    383 * @endcode
    -
    384 *
    -
    385 * @note
    -
    386 * If newmem flag is set, returned data will be malloced and should be
    -
    387 * deallocated by user. Otherwise returned pointer will point internal data
    -
    388 * buffer directly and should not be de-allocated by user. In thread-safe mode,
    -
    389 * always set newmem flag as true to make sure it works in race condition
    -
    390 * situation.
    -
    391 */
    -
    392void *qlisttbl_get(qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
    -
    393{
    -
    394 if (name == NULL) {
    -
    395 errno = EINVAL;
    -
    396 return NULL;
    -
    397 }
    -
    398
    -
    399 qlisttbl_lock(tbl);
    -
    400 void *data = NULL;
    -
    401 qlisttbl_obj_t *obj = findobj(tbl, name, NULL);
    -
    402 if (obj != NULL) {
    -
    403 // get data
    -
    404 if (newmem == true) {
    -
    405 data = malloc(obj->size);
    -
    406 if (data == NULL) {
    -
    407 errno = ENOMEM;
    -
    408 qlisttbl_unlock(tbl);
    -
    409 return NULL;
    -
    410 }
    -
    411 memcpy(data, obj->data, obj->size);
    -
    412 } else {
    -
    413 data = obj->data;
    -
    414 }
    -
    415
    -
    416 // set size
    -
    417 if (size != NULL) *size = obj->size;
    -
    418 }
    -
    419 qlisttbl_unlock(tbl);
    -
    420
    -
    421 if (data == NULL) {
    -
    422 errno = ENOENT;
    -
    423 }
    -
    424
    -
    425 return data;
    -
    426}
    -
    427
    -
    428/**
    -
    429 * qlisttbl->getstr(): Finds an object with given name and returns as
    -
    430 * string type.
    -
    431 *
    -
    432 * @param tbl qlisttbl container pointer.
    -
    433 * @param name element name.
    -
    434 * @param newmem whether or not to allocate memory for the data.
    -
    435 *
    -
    436 * @return a pointer of data if key is found, otherwise returns NULL.
    -
    437 * @retval errno will be set in error condition.
    -
    438 * - ENOENT : No such key found.
    -
    439 * - EINVAL : Invalid argument.
    -
    440 * - ENOMEM : Memory allocation failure.
    -
    441*/
    -
    442char *qlisttbl_getstr(qlisttbl_t *tbl, const char *name, bool newmem)
    -
    443{
    -
    444 return (char *)qlisttbl_get(tbl, name, NULL, newmem);
    -
    445}
    -
    446
    -
    447/**
    -
    448 * qlisttbl->getint(): Finds an object with given name and returns as
    -
    449 * integer type.
    -
    450 *
    -
    451 * @param tbl qlisttbl container pointer.
    -
    452 * @param name element name.
    -
    453 *
    -
    454 * @return an integer value of the integer object, otherwise returns 0.
    -
    455 * @retval errno will be set in error condition.
    -
    456 * - ENOENT : No such key found.
    -
    457 * - EINVAL : Invalid argument.
    -
    458 * - ENOMEM : Memory allocation failure.
    -
    459 */
    -
    460int64_t qlisttbl_getint(qlisttbl_t *tbl, const char *name)
    -
    461{
    -
    462 int64_t num = 0;
    -
    463 char *str = qlisttbl_getstr(tbl, name, true);
    -
    464 if (str != NULL) {
    -
    465 num = atoll(str);
    -
    466 free(str);
    -
    467 }
    -
    468 return num;
    -
    469}
    -
    470
    -
    471/**
    -
    472 * qlisttbl->getmulti(): Finds all objects with given name and return a array
    -
    473 * of objects.
    -
    474 *
    -
    475 * If there are duplicate keys in the table, this will return all
    -
    476 * the matched ones. The order of objects in return depends on setnextdir()
    -
    477 * setting. By default, the order is same(forward) as listed in the table.
    -
    478 *
    -
    479 * @param tbl qlisttbl container pointer.
    -
    480 * @param name element name.
    -
    481 * @param newmem whether or not to allocate memory for the data.
    -
    482 * @param numobjs the nuber of objects returned will be stored. (can be NULL)
    -
    483 *
    -
    484 * @return a pointer of data if key is found, otherwise returns NULL.
    -
    485 * @retval errno will be set in error condition.
    -
    486 * - ENOENT : No such key found.
    -
    487 * - EINVAL : Invalid argument.
    -
    488 * - ENOMEM : Memory allocation failure.
    -
    489 *
    -
    490 * @note
    -
    491 * The returned array of qlisttbl_data_t should be released by freemulti() call
    -
    492 * after use. Even you call getmulti() with newmem set false, freemulti() should
    -
    493 * be called all the times, so the object array itself can be released.
    -
    494 *
    -
    495 * @code
    -
    496 * size_t numobjs = 0;
    -
    497 * qlisttbl_data_t *objs = tbl->getmulti(tbl, "e2", true, &numobjs);
    -
    498 * for (i = 0; objs[i].data != NULL; i++) {
    -
    499 * printf("DATA=%s, SIZE=%zu\n", i, (char *)objs[i].data, objs[i].size);
    -
    500 * }
    -
    501 * tbl->freemulti(objs);
    -
    502 * @endcode
    -
    503 */
    -
    504qlisttbl_data_t *qlisttbl_getmulti(qlisttbl_t *tbl, const char *name, bool newmem,
    -
    505 size_t *numobjs)
    -
    506{
    -
    507 qlisttbl_data_t *objs = NULL; // objects container
    -
    508 size_t allocobjs = 0; // allocated number of objs
    -
    509 size_t numfound = 0; // number of keys found
    -
    510
    -
    511 qlisttbl_obj_t obj;
    -
    512 memset((void *)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    513 qlisttbl_lock(tbl);
    -
    514 while (tbl->getnext(tbl, &obj, name, newmem) == true) {
    -
    515 numfound++;
    -
    516
    -
    517 // allocate object array.
    -
    518 if (numfound >= allocobjs) {
    -
    519 if (allocobjs == 0) allocobjs = 10; // start from 10
    -
    520 else allocobjs *= 2; // double size
    -
    521 objs = (qlisttbl_data_t *)realloc(objs, sizeof(qlisttbl_data_t) * allocobjs);
    -
    522 if (objs == NULL) {
    -
    523 DEBUG("qlisttbl->getmulti(): Memory reallocation failure.");
    -
    524 errno = ENOMEM;
    -
    525 break;
    -
    526 }
    -
    527 }
    -
    528
    -
    529 // copy reference
    -
    530 qlisttbl_data_t *newobj = &objs[numfound - 1];
    -
    531 newobj->data = obj.data;
    -
    532 newobj->size = obj.size;
    -
    533 newobj->type = (newmem == false) ? 1 : 2;
    -
    534
    -
    535 // release resource
    -
    536 if (newmem == true) {
    -
    537 if (obj.name != NULL) free(obj.name);
    -
    538 }
    -
    539
    -
    540 // clear next block
    -
    541 newobj = &objs[numfound];
    -
    542 memset((void *)newobj, '\0', sizeof(qlisttbl_data_t));
    -
    543 newobj->type = 0; // mark, end of objects
    -
    544 }
    -
    545 qlisttbl_unlock(tbl);
    -
    546
    -
    547 // return found counter
    -
    548 if (numobjs != NULL) {
    -
    549 *numobjs = numfound;
    -
    550 }
    -
    551
    -
    552 if (numfound == 0) {
    -
    553 errno = ENOENT;
    -
    554 }
    -
    555
    -
    556 return objs;
    -
    557}
    -
    558
    -
    559/**
    -
    560 * qlisttbl->freemulti(): Deallocate object array returned by getmulti().
    -
    561 *
    -
    562 * @param objs pointer of array of qlisttbl_data_t.
    -
    563 *
    -
    564 * @code
    -
    565 * qlisttbl_data_t *objs = tbl->getmulti(tbl, "newmem is true", true, &numobjs);
    -
    566 * tbl->freemulti(objs); // frees allocated objects and object array itself.
    -
    567 *
    -
    568 * qlisttbl_data_t *objs = tbl->getmulti(tbl, "even newmem is false", false, &numobjs);
    -
    569 * tbl->freemulti(objs); // frees object array itself.
    -
    570 *
    -
    571 * @endcode
    -
    572 */
    -
    573void qlisttbl_freemulti(qlisttbl_data_t *objs)
    -
    574{
    -
    575 if (objs == NULL) return;
    -
    576
    -
    577 qlisttbl_data_t *obj;
    -
    578 for (obj = &objs[0]; obj->type == 2; obj++) {
    -
    579 if (obj->data != NULL) free(obj->data);
    -
    580 }
    -
    581
    -
    582 free(objs);
    -
    583}
    -
    584
    -
    585/**
    -
    586 * qlisttbl->remove(): Remove matched objects with given name.
    -
    587 *
    -
    588 * @param tbl qlisttbl container pointer.
    -
    589 * @param name element name.
    -
    590 *
    -
    591 * @return a number of removed objects.
    -
    592 */
    -
    593size_t qlisttbl_remove(qlisttbl_t *tbl, const char *name)
    -
    594{
    -
    595 if (name == NULL) return false;
    -
    596
    -
    597 size_t numremoved = 0;
    -
    598
    -
    599 qlisttbl_obj_t obj;
    -
    600 memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    601 qlisttbl_lock(tbl);
    -
    602 while (qlisttbl_getnext(tbl, &obj, name, false) == true) {
    -
    603 qlisttbl_removeobj(tbl, &obj);
    -
    604 numremoved++;
    -
    605 }
    -
    606 qlisttbl_unlock(tbl);
    -
    607
    -
    608 return numremoved;
    -
    609}
    -
    610
    -
    611/**
    -
    612 * qlisttbl->removeobj(): Remove objects with given object pointer.
    -
    613 *
    -
    614 * This call is useful when you want to remove an element while traversing a
    -
    615 * table using getnext(). So the address pointed by obj maybe different than
    -
    616 * the actual object in a table, but it's ok because we'll recalculate the
    -
    617 * actual object address by referring it's links.
    -
    618 *
    -
    619 * @param tbl qlisttbl container pointer.
    -
    620 * @param name element name.
    -
    621 *
    -
    622 * @return true if succeed on deletion, false if failed.
    -
    623 * @retval errno will be set in error condition.
    -
    624 * - ENOENT : No such key found.
    -
    625 *
    -
    626 * @code
    -
    627 * qlisttbl_obj_t obj;
    -
    628 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    629 * tbl->lock(tbl);
    -
    630 * while(tbl->getnext(tbl, &obj, NULL, true) == true) {
    -
    631 * tbl->removeobj(tbl, &obj); // this will remove all entries in the table
    -
    632 * }
    -
    633 * tbl->unlock(tbl);
    -
    634 * @endcode
    -
    635 */
    -
    636bool qlisttbl_removeobj(qlisttbl_t *tbl, const qlisttbl_obj_t *obj)
    -
    637{
    -
    638 if (obj == NULL) return false;
    -
    639
    -
    640 qlisttbl_lock(tbl);
    -
    641
    -
    642 // copy chains
    -
    643 qlisttbl_obj_t *prev = obj->prev;
    -
    644 qlisttbl_obj_t *next = obj->next;
    -
    645
    -
    646 // find this object
    -
    647 qlisttbl_obj_t *this = NULL;
    -
    648 if (prev != NULL) this = prev->next;
    -
    649 else if (next != NULL) this = next->prev;
    -
    650 else this = tbl->first; // table has only one object.
    -
    651
    -
    652 // double check
    -
    653 if (this == NULL) {
    -
    654 qlisttbl_unlock(tbl);
    -
    655 DEBUG("qlisttbl->removeobj(): Can't veryfy object.");
    -
    656 errno = ENOENT;
    -
    657 return false;
    -
    658 }
    -
    659
    -
    660 // adjust chain links
    -
    661 if (prev == NULL) tbl->first = next; // if the object is first one
    -
    662 else prev->next = next; // not the first one
    -
    663
    -
    664 if (next == NULL) tbl->last = prev; // if the object is last one
    -
    665 else next->prev = prev; // not the first one
    -
    666
    -
    667 // adjust counter
    -
    668 tbl->num--;
    -
    669
    -
    670 qlisttbl_unlock(tbl);
    -
    671
    -
    672 // free object
    -
    673 free(this->name);
    -
    674 free(this->data);
    -
    675 free(this);
    -
    676
    -
    677 return true;
    -
    678}
    -
    679
    -
    680/**
    -
    681 * qlisttbl->getnext(): Get next element.
    -
    682 *
    -
    683 * Default searching direction is backward, from the bottom to top
    -
    684 * unless QLISTTBL_LOOKUPFORWARD option was specified.
    -
    685 *
    -
    686 * @param tbl qlisttbl container pointer.
    -
    687 * @param obj found data will be stored in this object
    -
    688 * @param name element name., if key name is NULL search every objects in
    -
    689 * the table.
    -
    690 * @param newmem whether or not to allocate memory for the data.
    -
    691 *
    -
    692 * @return true if found otherwise returns false
    -
    693 * @retval errno will be set in error condition.
    -
    694 * - ENOENT : No next element.
    -
    695 * - ENOMEM : Memory allocation failure.
    -
    696 *
    -
    697 * @note
    -
    698 * The obj should be initialized with 0 by using memset() before first call.
    -
    699 * If newmem flag is true, user should de-allocate obj.name and obj.data
    -
    700 * resources.
    -
    701 *
    -
    702 * @code
    -
    703 * qlisttbl_t *tbl = qlisttbl();
    -
    704 * (...add data into table...)
    -
    705 *
    -
    706 * // non-thread usages
    -
    707 * qlisttbl_obj_t obj;
    -
    708 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    709 * while(tbl->getnext(tbl, &obj, NULL, false) == true) {
    -
    710 * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    -
    711 * obj.name, (char*)obj.data, obj.size);
    -
    712 * }
    -
    713 *
    -
    714 * // thread model with particular key search
    -
    715 * qlisttbl_obj_t obj;
    -
    716 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    717 * tbl->lock(tbl);
    -
    718 * while(tbl->getnext(tbl, &obj, "key_name", true) == true) {
    -
    719 * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    -
    720 * obj.name, (char*)obj.data, obj.size);
    -
    721 * free(obj.name);
    -
    722 * free(obj.data);
    -
    723 * }
    -
    724 * tbl->unlock(tbl);
    -
    725 * @endcode
    -
    726 */
    -
    727bool qlisttbl_getnext(qlisttbl_t *tbl, qlisttbl_obj_t *obj, const char *name,
    -
    728 bool newmem)
    -
    729{
    -
    730 if (obj == NULL) return NULL;
    -
    731
    -
    732 qlisttbl_lock(tbl);
    -
    733
    -
    734 qlisttbl_obj_t *cont = NULL;
    -
    735 if (obj->size == 0) { // first time call
    -
    736 if (name == NULL) { // full scan
    -
    737 cont = (tbl->lookupforward) ? tbl->first : tbl->last;
    -
    738 } else { // name search
    -
    739 cont = findobj(tbl, name, NULL);
    -
    740 }
    -
    741 } else { // next call
    -
    742 cont = (tbl->lookupforward) ? obj->next : obj->prev;
    -
    743 }
    -
    744
    -
    745 if (cont == NULL) {
    -
    746 errno = ENOENT;
    -
    747 qlisttbl_unlock(tbl);
    -
    748 return false;
    -
    749 }
    -
    750
    -
    751 uint32_t hash = (name != NULL) ? qhashmurmur3_32(name, strlen(name)) : 0;
    -
    752
    -
    753 bool ret = false;
    -
    754 while (cont != NULL) {
    -
    755 if (name == NULL || tbl->namematch(cont, name, hash) == true) {
    -
    756 if (newmem == true) {
    -
    757 obj->name = strdup(cont->name);
    -
    758 obj->data = malloc(cont->size);
    -
    759 if (obj->name == NULL || obj->data == NULL) {
    -
    760 if (obj->name != NULL) free(obj->name);
    -
    761 if (obj->data != NULL) free(obj->data);
    -
    762 obj->name = NULL;
    -
    763 obj->data = NULL;
    -
    764 errno = ENOMEM;
    -
    765 break;
    -
    766 }
    -
    767 memcpy(obj->data, cont->data, cont->size);
    -
    768 } else {
    -
    769 obj->name = cont->name;
    -
    770 obj->data = cont->data;
    -
    771 }
    -
    772 obj->hash = cont->hash;
    -
    773 obj->size = cont->size;
    -
    774 obj->prev = cont->prev;
    -
    775 obj->next = cont->next;
    -
    776
    -
    777 ret = true;
    -
    778 break;
    -
    779 }
    -
    780
    -
    781 cont = (tbl->lookupforward) ? cont->next : cont->prev;
    -
    782 }
    -
    783 qlisttbl_unlock(tbl);
    -
    784
    -
    785 if (ret == false) {
    -
    786 errno = ENOENT;
    -
    787 }
    -
    788
    -
    789 return ret;
    -
    790}
    -
    791
    -
    792/**
    -
    793 * qlisttbl->size(): Returns the number of elements in this table.
    -
    794 *
    -
    795 * @param tbl qlisttbl container pointer.
    -
    796 *
    -
    797 * @return the number of elements in this table.
    -
    798 */
    -
    799size_t qlisttbl_size(qlisttbl_t *tbl)
    -
    800{
    -
    801 return tbl->num;
    -
    802}
    -
    803
    -
    804/**
    -
    805 * qlisttbl->sort(): Sort keys in this table.
    -
    806 *
    -
    807 * @param tbl qlisttbl container pointer.
    -
    808 *
    -
    809 * @note
    -
    810 * It will sort the table in ascending manner, if you need descending order somehow,
    -
    811 * lookup-backword option will do the job without changing the order in the table.
    -
    812 *
    -
    813 * @code
    -
    814 * The appearence order of duplicated keys will be preserved in a sored table.
    -
    815 * See how duplicated key 'b' is ordered in below example
    -
    816 *
    -
    817 * [Unsorted] [sorted ASC] [sorted DES]
    -
    818 * d = 1 a = 2 d = 1
    -
    819 * a = 2 b = 3 c = 5
    -
    820 * b = 3 b = 4 b = 3
    -
    821 * b = 4 b = 6 b = 4
    -
    822 * c = 5 c = 5 b = 6
    -
    823 * b = 6 d = 1 a = 2
    -
    824 * @endcode
    -
    825 */
    -
    826void qlisttbl_sort(qlisttbl_t *tbl)
    -
    827{
    -
    828 // run bubble sort
    -
    829 qlisttbl_lock(tbl);
    -
    830 qlisttbl_obj_t *obj1, *obj2;
    -
    831 qlisttbl_obj_t tmpobj;
    -
    832 int n, n2, i;
    -
    833 for (n = tbl->num; n > 0;) {
    -
    834 n2 = 0;
    -
    835 for (i = 0, obj1 = tbl->first; i < (n - 1); i++, obj1 = obj1->next) {
    -
    836 obj2 = obj1->next; // this can't be null.
    -
    837 if (tbl->namecmp(obj1->name, obj2->name) > 0) {
    -
    838 // swapping contents is faster than adjusting links.
    -
    839 tmpobj = *obj1;
    -
    840 obj1->hash = obj2->hash;
    -
    841 obj1->name = obj2->name;
    -
    842 obj1->data = obj2->data;
    -
    843 obj1->size = obj2->size;
    -
    844 obj2->hash = tmpobj.hash;
    -
    845 obj2->name = tmpobj.name;
    -
    846 obj2->data = tmpobj.data;
    -
    847 obj2->size = tmpobj.size;
    -
    848
    -
    849 n2 = i + 1;
    -
    850 }
    -
    851 }
    -
    852 n = n2; // skip sorted tailing elements
    -
    853 }
    -
    854 qlisttbl_unlock(tbl);
    -
    855}
    -
    856
    -
    857/**
    -
    858 * qlisttbl->clear(): Removes all of the elements from this table.
    -
    859 *
    -
    860 * @param tbl qlisttbl container pointer.
    -
    861 */
    -
    862void qlisttbl_clear(qlisttbl_t *tbl)
    -
    863{
    -
    864 qlisttbl_lock(tbl);
    -
    865 qlisttbl_obj_t *obj;
    -
    866 for (obj = tbl->first; obj != NULL;) {
    -
    867 qlisttbl_obj_t *next = obj->next;
    -
    868 free(obj->name);
    -
    869 free(obj->data);
    -
    870 free(obj);
    -
    871 obj = next;
    -
    872 }
    -
    873
    -
    874 tbl->num = 0;
    -
    875 tbl->first = NULL;
    -
    876 tbl->last = NULL;
    -
    877 qlisttbl_unlock(tbl);
    -
    878}
    -
    879
    -
    880/**
    -
    881 * qlisttbl->save(): Save qlisttbl as plain text format
    -
    882 * Dumping direction is always forward(from the top to the bottom) to preserve
    -
    883 * the order when we load the file again.
    -
    884 *
    -
    885 * @param tbl qlisttbl container pointer.
    -
    886 * @param filepath save file path
    -
    887 * @param sepchar separator character between name and value. normally '=' is
    -
    888 * used.
    -
    889 * @param encode flag for encoding data . false can be used if the all data
    -
    890 * are string or integer type and has no new line. otherwise
    -
    891 * true must be set.
    -
    892 *
    -
    893 * @return true if successful, otherwise returns false.
    -
    894 */
    -
    895bool qlisttbl_save(qlisttbl_t *tbl, const char *filepath, char sepchar,
    -
    896 bool encode)
    -
    897{
    -
    898 if (filepath == NULL) {
    -
    899 errno = EINVAL;
    -
    900 return false;
    -
    901 }
    -
    902
    -
    903 int fd;
    -
    904 if ((fd = open(filepath, O_CREAT|O_WRONLY|O_TRUNC, (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))) < 0) {
    -
    905 DEBUG("qlisttbl->save(): Can't open file %s", filepath);
    -
    906 return false;
    -
    907 }
    -
    908
    -
    909 char *gmtstr = qtime_gmt_str(0);
    -
    910 qio_printf(fd, -1, "# %s %s\n", filepath, gmtstr);
    -
    911 free(gmtstr);
    -
    912
    -
    913 qlisttbl_lock(tbl);
    -
    914 qlisttbl_obj_t *obj;
    -
    915 for (obj = tbl->first; obj; obj = obj->next) {
    -
    916 char *encval;
    -
    917 if (encode == true) encval = qurl_encode(obj->data, obj->size);
    -
    918 else encval = obj->data;
    -
    919 qio_printf(fd, -1, "%s%c%s\n", obj->name, sepchar, encval);
    -
    920 if (encode == true) free(encval);
    -
    921 }
    -
    922 qlisttbl_unlock(tbl);
    -
    923
    -
    924 close(fd);
    -
    925 return true;
    -
    926}
    -
    927
    -
    928/**
    -
    929 * qlisttbl->load(): Load and append entries from given filepath
    -
    930 * Loading direction is always appending at the bottom of the table to preserve
    -
    931 * the order as it was.
    -
    932 *
    -
    933 * @param tbl qlisttbl container pointer.
    -
    934 * @param filepath save file path
    -
    935 * @param sepchar separator character between name and value. normally '=' is
    -
    936 * used
    -
    937 * @param decode flag for decoding data
    -
    938 *
    -
    939 * @return the number of loaded entries, otherwise returns -1.
    -
    940 */
    -
    941ssize_t qlisttbl_load(qlisttbl_t *tbl, const char *filepath, char sepchar,
    -
    942 bool decode)
    -
    943{
    -
    944 // load file
    -
    945 char *str = qfile_load(filepath, NULL);
    -
    946 if (str == NULL) return -1;
    -
    947
    -
    948 // parse
    -
    949 qlisttbl_lock(tbl);
    -
    950 char *offset, *buf;
    -
    951 int cnt = 0;
    -
    952 for (offset = str; *offset != '\0'; ) {
    -
    953 // get one line into buf
    -
    954 for (buf = offset; *offset != '\n' && *offset != '\0'; offset++);
    -
    955 if (*offset != '\0') {
    -
    956 *offset = '\0';
    -
    957 offset++;
    -
    958 }
    -
    959 qstrtrim(buf);
    -
    960
    -
    961 // skip blank or comment line
    -
    962 if ((buf[0] == '#') || (buf[0] == '\0')) continue;
    -
    963
    -
    964 // parse
    -
    965 char *data = strdup(buf);
    -
    966 char *name = _q_makeword(data, sepchar);
    -
    967 qstrtrim(data);
    -
    968 qstrtrim(name);
    -
    969 if (decode == true) qurl_decode(data);
    -
    970
    -
    971 // add to the table.
    -
    972 qlisttbl_put(tbl, name, data, strlen(data) + 1);
    -
    973
    -
    974 free(name);
    -
    975 free(data);
    -
    976 }
    -
    977 qlisttbl_unlock(tbl);
    -
    978 free(str);
    -
    979
    -
    980 return cnt;
    -
    981}
    -
    982
    -
    983/**
    -
    984 * qlisttbl->debug(): Print out stored elements for debugging purpose.
    -
    985 *
    -
    986 * @param tbl qlisttbl container pointer.
    -
    987 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    988 *
    -
    989 * @return true if successful, otherwise returns false.
    -
    990 * @retval errno will be set in error condition.
    -
    991 * - EIO : Invalid output stream.
    -
    992 */
    -
    993bool qlisttbl_debug(qlisttbl_t *tbl, FILE *out)
    -
    994{
    -
    995 if (out == NULL) {
    -
    996 errno = EIO;
    -
    997 return false;
    -
    998 }
    -
    999
    -
    1000 qlisttbl_lock(tbl);
    -
    1001 qlisttbl_obj_t *obj;
    -
    1002 for (obj = tbl->first; obj; obj = obj->next) {
    -
    1003 fprintf(out, "%s=" , obj->name);
    -
    1004 _q_textout(out, obj->data, obj->size, MAX_HUMANOUT);
    -
    1005 fprintf(out, " (%zu, %08x)\n" , obj->size, obj->hash);
    -
    1006 }
    -
    1007 qlisttbl_unlock(tbl);
    -
    1008
    -
    1009 return true;
    -
    1010}
    -
    1011
    -
    1012/**
    -
    1013 * qlisttbl->lock(): Enter critical section.
    -
    1014 *
    -
    1015 * @param tbl qlisttbl container pointer.
    -
    1016 *
    -
    1017 * @note
    -
    1018 * Normally explicit locking is only needed when traverse all the
    -
    1019 * elements with qlisttbl->getnext().
    -
    1020 */
    -
    1021void qlisttbl_lock(qlisttbl_t *tbl)
    -
    1022{
    -
    1023 Q_MUTEX_ENTER(tbl->qmutex);
    -
    1024}
    -
    1025
    -
    1026/**
    -
    1027 * qlisttbl->unlock(): Leave critical section.
    -
    1028 *
    -
    1029 * @param tbl qlisttbl container pointer.
    -
    1030 */
    -
    1031void qlisttbl_unlock(qlisttbl_t *tbl)
    -
    1032{
    -
    1033 Q_MUTEX_LEAVE(tbl->qmutex);
    -
    1034}
    -
    1035
    -
    1036/**
    -
    1037 * qlisttbl->free(): Free qlisttbl_t
    -
    1038 *
    -
    1039 * @param tbl qlisttbl container pointer.
    -
    1040 */
    -
    1041void qlisttbl_free(qlisttbl_t *tbl)
    -
    1042{
    -
    1043 qlisttbl_clear(tbl);
    -
    1044 Q_MUTEX_DESTROY(tbl->qmutex);
    -
    1045 free(tbl);
    -
    1046}
    -
    1047
    -
    1048#ifndef _DOXYGEN_SKIP
    -
    1049
    -
    1050// lock must be obtained from caller
    -
    1051static qlisttbl_obj_t *newobj(const char *name, const void *data, size_t size)
    -
    1052{
    -
    1053 if (name == NULL || data == NULL || size <= 0) {
    -
    1054 errno = EINVAL;
    -
    1055 return false;
    -
    1056 }
    -
    1057
    -
    1058 // make a new object
    -
    1059 char *dup_name = strdup(name);
    -
    1060 void *dup_data = malloc(size);
    -
    1061 qlisttbl_obj_t *obj = (qlisttbl_obj_t *)malloc(sizeof(qlisttbl_obj_t));
    -
    1062 if (dup_name == NULL || dup_data == NULL || obj == NULL) {
    -
    1063 if (dup_name != NULL) free(dup_name);
    -
    1064 if (dup_data != NULL) free(dup_data);
    -
    1065 if (obj != NULL) free(obj);
    -
    1066 errno = ENOMEM;
    -
    1067 return NULL;
    -
    1068 }
    -
    1069 memcpy(dup_data, data, size);
    -
    1070 memset((void *)obj, '\0', sizeof(qlisttbl_obj_t));
    -
    1071
    -
    1072 // obj->hash = qhashmurmur3_32(dup_name);
    -
    1073 obj->name = dup_name;
    -
    1074 obj->data = dup_data;
    -
    1075 obj->size = size;
    -
    1076
    -
    1077 return obj;
    -
    1078}
    -
    1079
    -
    1080// lock must be obtained from caller
    -
    1081static bool insertobj(qlisttbl_t *tbl, qlisttbl_obj_t *obj)
    -
    1082{
    -
    1083 // update hash
    -
    1084 obj->hash = qhashmurmur3_32(obj->name, strlen(obj->name));
    -
    1085
    -
    1086 qlisttbl_obj_t *prev = obj->prev;
    -
    1087 qlisttbl_obj_t *next = obj->next;
    -
    1088
    -
    1089 if (prev == NULL) tbl->first = obj;
    -
    1090 else prev->next = obj;
    -
    1091
    -
    1092 if (next == NULL) tbl->last = obj;
    -
    1093 else next->prev = obj;
    -
    1094
    -
    1095 // increase counter
    -
    1096 tbl->num++;
    -
    1097
    -
    1098 return true;
    -
    1099}
    -
    1100
    -
    1101// lock must be obtained from caller
    -
    1102static qlisttbl_obj_t *findobj(qlisttbl_t *tbl, const char *name, qlisttbl_obj_t *retobj)
    -
    1103{
    -
    1104 if (retobj != NULL) {
    -
    1105 memset((void *)retobj, '\0', sizeof(qlisttbl_obj_t));
    -
    1106 }
    -
    1107
    -
    1108 if (name == NULL || tbl->num == 0) {
    -
    1109 errno = ENOENT;
    -
    1110 return NULL;
    -
    1111 }
    -
    1112
    -
    1113 uint32_t hash = qhashmurmur3_32(name, strlen(name));
    -
    1114 qlisttbl_obj_t *obj = (tbl->lookupforward) ? tbl->first : tbl->last;
    -
    1115 while (obj != NULL) {
    -
    1116 // name string will be compared only if the hash matches.
    -
    1117 if (tbl->namematch(obj, name, hash) == true) {
    -
    1118 if (retobj != NULL) {
    -
    1119 *retobj = *obj;
    -
    1120 }
    -
    1121 return obj;
    -
    1122 }
    -
    1123 obj = (tbl->lookupforward)? obj->next : obj->prev;
    -
    1124 }
    -
    1125
    -
    1126 // not found, set prev and next chain.
    -
    1127 if (retobj != NULL) {
    -
    1128 if (tbl->inserttop) {
    -
    1129 retobj->prev = NULL;
    -
    1130 retobj->next = tbl->first;
    -
    1131 } else {
    -
    1132 retobj->prev = tbl->last;
    -
    1133 retobj->next = NULL;
    -
    1134 }
    -
    1135 }
    -
    1136 errno = ENOENT;
    -
    1137
    -
    1138 return NULL;
    -
    1139}
    -
    1140
    -
    1141// key comp
    -
    1142static bool namematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash)
    -
    1143{
    -
    1144 if ((obj->hash == hash) && !strcmp(obj->name, name)) {
    -
    1145 return true;
    -
    1146 }
    -
    1147 return false;
    -
    1148}
    -
    1149
    -
    1150static bool namecasematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash)
    -
    1151{
    -
    1152 if (!strcasecmp(obj->name, name)) {
    -
    1153 return true;
    -
    1154 }
    -
    1155 return false;
    -
    1156}
    -
    1157
    -
    1158#endif /* _DOXYGEN_SKIP */
    -
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition qencode.c:125
    -
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition qencode.c:192
    -
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition qfile.c:159
    -
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition qhash.c:263
    -
    ssize_t qio_printf(int fd, int timeoutms, const char *format,...)
    Formatted output to a file descriptor.
    Definition qio.c:325
    -
    qlisttbl_data_t * qlisttbl_getmulti(qlisttbl_t *tbl, const char *name, bool newmem, size_t *numobjs)
    qlisttbl->getmulti(): Finds all objects with given name and return a array of objects.
    Definition qlisttbl.c:504
    -
    bool qlisttbl_put(qlisttbl_t *tbl, const char *name, const void *data, size_t size)
    qlisttbl->put(): Put an element to this table.
    Definition qlisttbl.c:249
    -
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition qlisttbl.c:150
    -
    bool qlisttbl_putstrf(qlisttbl_t *tbl, const char *name, const char *format,...)
    qlisttbl->putstrf(): Put a formatted string into this table.
    Definition qlisttbl.c:314
    -
    void qlisttbl_sort(qlisttbl_t *tbl)
    qlisttbl->sort(): Sort keys in this table.
    Definition qlisttbl.c:826
    -
    bool qlisttbl_putint(qlisttbl_t *tbl, const char *name, int64_t num)
    qlisttbl->putInt(): Put an integer into this table as string type.
    Definition qlisttbl.c:345
    -
    void qlisttbl_freemulti(qlisttbl_data_t *objs)
    qlisttbl->freemulti(): Deallocate object array returned by getmulti().
    Definition qlisttbl.c:573
    -
    bool qlisttbl_removeobj(qlisttbl_t *tbl, const qlisttbl_obj_t *obj)
    qlisttbl->removeobj(): Remove objects with given object pointer.
    Definition qlisttbl.c:636
    -
    size_t qlisttbl_size(qlisttbl_t *tbl)
    qlisttbl->size(): Returns the number of elements in this table.
    Definition qlisttbl.c:799
    -
    void qlisttbl_free(qlisttbl_t *tbl)
    qlisttbl->free(): Free qlisttbl_t
    Definition qlisttbl.c:1041
    -
    void qlisttbl_lock(qlisttbl_t *tbl)
    qlisttbl->lock(): Enter critical section.
    Definition qlisttbl.c:1021
    -
    void qlisttbl_unlock(qlisttbl_t *tbl)
    qlisttbl->unlock(): Leave critical section.
    Definition qlisttbl.c:1031
    -
    char * qlisttbl_getstr(qlisttbl_t *tbl, const char *name, bool newmem)
    qlisttbl->getstr(): Finds an object with given name and returns as string type.
    Definition qlisttbl.c:442
    -
    bool qlisttbl_putstr(qlisttbl_t *tbl, const char *name, const char *str)
    qlisttbl->putstr(): Put a string into this table.
    Definition qlisttbl.c:296
    -
    void qlisttbl_clear(qlisttbl_t *tbl)
    qlisttbl->clear(): Removes all of the elements from this table.
    Definition qlisttbl.c:862
    -
    bool qlisttbl_save(qlisttbl_t *tbl, const char *filepath, char sepchar, bool encode)
    qlisttbl->save(): Save qlisttbl as plain text format Dumping direction is always forward(from the top...
    Definition qlisttbl.c:895
    -
    bool qlisttbl_debug(qlisttbl_t *tbl, FILE *out)
    qlisttbl->debug(): Print out stored elements for debugging purpose.
    Definition qlisttbl.c:993
    -
    bool qlisttbl_getnext(qlisttbl_t *tbl, qlisttbl_obj_t *obj, const char *name, bool newmem)
    qlisttbl->getnext(): Get next element.
    Definition qlisttbl.c:727
    -
    size_t qlisttbl_remove(qlisttbl_t *tbl, const char *name)
    qlisttbl->remove(): Remove matched objects with given name.
    Definition qlisttbl.c:593
    -
    void * qlisttbl_get(qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
    qlisttbl->get(): Finds an object with given name.
    Definition qlisttbl.c:392
    -
    ssize_t qlisttbl_load(qlisttbl_t *tbl, const char *filepath, char sepchar, bool decode)
    qlisttbl->load(): Load and append entries from given filepath Loading direction is always appending a...
    Definition qlisttbl.c:941
    -
    int64_t qlisttbl_getint(qlisttbl_t *tbl, const char *name)
    qlisttbl->getint(): Finds an object with given name and returns as integer type.
    Definition qlisttbl.c:460
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    -
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition qtime.c:174
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qlisttbl.c Linked-list-table implementation.
    +
    31  *
    +
    32  * qlisttbl container is a Linked-List-Table implementation.
    +
    33  * Which maps keys to values. Key is a string and value is any non-null object.
    +
    34  * These elements are stored sequentially in Doubly-Linked-List data structure.
    +
    35  *
    +
    36  * Compared to Hash-Table, List-Table is efficient when you need to keep
    +
    37  * duplicated keys since Hash-Table only keep unique keys. Of course, qlisttbl
    +
    38  * supports both unique keys and key duplication.
    +
    39  *
    +
    40  * @code
    +
    41  * [Conceptional Data Structure Diagram]
    +
    42  *
    +
    43  * last~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
    +
    44  * |
    +
    45  * +-----------+ doubly +-----------+ doubly +-v---------+
    +
    46  * first~~~|~> 0 <~|~~~~~~~~~~|~> 1 <~|~~~~~~~~~~|~> N |
    +
    47  * +--|-----|--+ linked +--|-----|--+ linked +--|-----|--+
    +
    48  * | | | | | |
    +
    49  * +-----v-+ +-v-------+ | | +-----v-+ +-v-------+
    +
    50  * | KEY A | | VALUE A | | | | KEY N | | VALUE N |
    +
    51  * +-------+ +---------+ | | +-------+ +---------+
    +
    52  * +-----v-+ +-v-------+
    +
    53  * | KEY B | | VALUE B |
    +
    54  * +-------+ +---------+
    +
    55  * @endcode
    +
    56  *
    +
    57  * @code
    +
    58  * // create a list table.
    +
    59  * qlisttbl_t *tbl = qlisttbl(QLISTTBL_THREADSAFE);
    +
    60  *
    +
    61  * // insert elements (key duplication allowed)
    +
    62  * tbl->put(tbl, "e1", "a", strlen("e1")+1); // equal to putstr();
    +
    63  * tbl->putstr(tbl, "e2", "b");
    +
    64  * tbl->putstr(tbl, "e2", "c");
    +
    65  * tbl->putstr(tbl, "e3", "d");
    +
    66  *
    +
    67  * // debug output
    +
    68  * tbl->debug(tbl, stdout, true);
    +
    69  *
    +
    70  * // get element
    +
    71  * printf("get('e2') : %s\n", (char*)tbl->get(tbl, "e2", NULL, false));
    +
    72  * printf("getstr('e2') : %s\n", tbl->getstr(tbl, "e2", false));
    +
    73  *
    +
    74  * // get all matching elements
    +
    75  * qlisttbl_data_t *objs = tbl->getmulti(tbl, "e2", true, NULL);
    +
    76  * for (i = 0; objs[i].data != NULL; i++) {
    +
    77  * printf("getmulti('e2')[%d] : %s (size=%d)\n",
    +
    78  * i, (char *)objs[i].data, (int)objs[i].size);
    +
    79  * }
    +
    80  * tbl->freemulti(objs);
    +
    81  *
    +
    82  * // find every 'e2' elements
    +
    83  * qlisttbl_obj_t obj;
    +
    84  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    85  * while(tbl->getnext(tbl, &obj, "e2", false) == true) {
    +
    86  * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    +
    87  * obj.name, (char*)obj.data, obj.size);
    +
    88  * }
    +
    89  *
    +
    90  * // free object
    +
    91  * tbl->free(tbl);
    +
    92  * @endcode
    +
    93  */
    +
    94 
    +
    95 #include <stdio.h>
    +
    96 #include <stdlib.h>
    +
    97 #include <stdbool.h>
    +
    98 #include <stdint.h>
    +
    99 #include <inttypes.h>
    +
    100 #include <string.h>
    +
    101 #include <stdarg.h>
    +
    102 #include <unistd.h>
    +
    103 #include <fcntl.h>
    +
    104 #include <sys/stat.h>
    +
    105 #include <assert.h>
    +
    106 #include <errno.h>
    +
    107 #include "qinternal.h"
    +
    108 #include "utilities/qencode.h"
    +
    109 #include "utilities/qfile.h"
    +
    110 #include "utilities/qhash.h"
    +
    111 #include "utilities/qio.h"
    +
    112 #include "utilities/qstring.h"
    +
    113 #include "utilities/qtime.h"
    +
    114 #include "containers/qlisttbl.h"
    +
    115 
    +
    116 #ifndef _DOXYGEN_SKIP
    +
    117 
    +
    118 static qlisttbl_obj_t *newobj(const char *name, const void *data, size_t size);
    +
    119 static bool insertobj(qlisttbl_t *tbl, qlisttbl_obj_t *obj);
    +
    120 static qlisttbl_obj_t *findobj(qlisttbl_t *tbl, const char *name, qlisttbl_obj_t *retobj);
    +
    121 
    +
    122 static bool namematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash);
    +
    123 static bool namecasematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash);
    +
    124 
    +
    125 #endif
    +
    126 
    +
    127 /**
    +
    128  * Create a new Q_LIST linked-list container
    +
    129  *
    +
    130  * @param options combination of initialization options.
    +
    131  *
    +
    132  * @return a pointer of malloced qlisttbl_t structure in case of successful,
    +
    133  * otherwise returns NULL.
    +
    134  * @retval errno will be set in error condition.
    +
    135  * - ENOMEM : Memory allocation failure.
    +
    136  *
    +
    137  * @code
    +
    138  * qlisttbl_t *tbl = qlisttbl(0);
    +
    139  * qlisttbl_t *threadsafe_tbl = qlisttbl(QLISTTBL_THREADSAFE);
    +
    140  * @endcode
    +
    141  *
    +
    142  * @note
    +
    143  * Available options:
    +
    144  * - QLISTTBL_THREADSAFE - make it thread-safe.
    +
    145  * - QLISTTBL_UNIQUE - keys are all unique. replace same key
    +
    146  * - QLISTTBL_CASEINSENSITIVE - key is case insensitive
    +
    147  * - QLISTTBL_INSERTTOP - insert new key at the top
    +
    148  * - QLISTTBL_LOOKUPFORWARD - find key from the top
    +
    149  */
    +
    150 qlisttbl_t *qlisttbl(int options)
    +
    151 {
    +
    152  qlisttbl_t *tbl = (qlisttbl_t *)calloc(1, sizeof(qlisttbl_t));
    +
    153  if (tbl == NULL) {
    +
    154  errno = ENOMEM;
    +
    155  return NULL;
    +
    156  }
    +
    157 
    +
    158  // assign member methods.
    +
    159  tbl->put = qlisttbl_put;
    +
    160  tbl->putstr = qlisttbl_putstr;
    +
    161  tbl->putstrf = qlisttbl_putstrf;
    +
    162  tbl->putint = qlisttbl_putint;
    +
    163 
    +
    164  tbl->get = qlisttbl_get;
    +
    165  tbl->getstr = qlisttbl_getstr;
    +
    166  tbl->getint = qlisttbl_getint;
    +
    167 
    +
    168  tbl->getmulti = qlisttbl_getmulti;
    +
    169  tbl->freemulti = qlisttbl_freemulti;
    +
    170 
    +
    171  tbl->remove = qlisttbl_remove;
    +
    172  tbl->removeobj = qlisttbl_removeobj;
    +
    173 
    +
    174  tbl->getnext = qlisttbl_getnext;
    +
    175 
    +
    176  tbl->size = qlisttbl_size;
    +
    177  tbl->sort = qlisttbl_sort;
    +
    178  tbl->clear = qlisttbl_clear;
    +
    179  tbl->save = qlisttbl_save;
    +
    180  tbl->load = qlisttbl_load;
    +
    181 
    +
    182  tbl->debug = qlisttbl_debug;
    +
    183 
    +
    184  tbl->lock = qlisttbl_lock;
    +
    185  tbl->unlock = qlisttbl_unlock;
    +
    186 
    +
    187  tbl->free = qlisttbl_free;
    +
    188 
    +
    189  // assign private methods.
    +
    190  tbl->namematch = namematch;
    +
    191  tbl->namecmp = strcmp;
    +
    192 
    +
    193  // handle options.
    +
    194  if (options & QLISTTBL_THREADSAFE) {
    +
    195  Q_MUTEX_NEW(tbl->qmutex, true);
    +
    196  if (tbl->qmutex == NULL) {
    +
    197  errno = ENOMEM;
    +
    198  free(tbl);
    +
    199  return NULL;
    +
    200  }
    +
    201  }
    +
    202  if (options & QLISTTBL_UNIQUE) {
    +
    203  tbl->unique = true;
    +
    204  }
    +
    205  if (options & QLISTTBL_CASEINSENSITIVE) {
    +
    206  tbl->namematch = namecasematch;
    +
    207  tbl->namecmp = strcasecmp;
    +
    208  }
    +
    209  if (options & QLISTTBL_INSERTTOP) {
    +
    210  tbl->inserttop = true;
    +
    211  }
    +
    212  if (options & QLISTTBL_LOOKUPFORWARD) {
    +
    213  tbl->lookupforward = true;
    +
    214  }
    +
    215 
    +
    216  return tbl;
    +
    217 }
    +
    218 
    +
    219 /**
    +
    220  * qlisttbl->put(): Put an element to this table.
    +
    221  *
    +
    222  * @param tbl qlisttbl container pointer.
    +
    223  * @param name element name.
    +
    224  * @param data a pointer which points data memory.
    +
    225  * @param size size of the data.
    +
    226  *
    +
    227  * @return true if successful, otherwise returns false.
    +
    228  * @retval errno will be set in error condition.
    +
    229  * - ENOMEM : Memory allocation failure.
    +
    230  * - EINVAL : Invalid argument.
    +
    231  *
    +
    232  * @code
    +
    233  * struct my_obj {
    +
    234  * int a, b, c;
    +
    235  * };
    +
    236  *
    +
    237  * // create a sample object.
    +
    238  * struct my_obj obj;
    +
    239  *
    +
    240  * // create a table and add a sample object.
    +
    241  * qlisttbl_t *tbl = qlisttbl();
    +
    242  * tbl->put(tbl, "obj1", &obj, sizeof(struct my_obj));
    +
    243  * @endcode
    +
    244  *
    +
    245  * @note
    +
    246  * The default behavior is adding an object at the end of this table
    +
    247  * unless QLISTTBL_INSERTTOP option was given.
    +
    248  */
    +
    249 bool qlisttbl_put(qlisttbl_t *tbl, const char *name, const void *data, size_t size)
    +
    250 {
    +
    251  // make new object table
    +
    252  qlisttbl_obj_t *obj = newobj(name, data, size);
    +
    253  if (obj == NULL) {
    +
    254  return false;
    +
    255  }
    +
    256 
    +
    257  // lock table
    +
    258  qlisttbl_lock(tbl);
    +
    259 
    +
    260  // if unique flag is set, remove same key
    +
    261  if (tbl->unique == true) qlisttbl_remove(tbl, name);
    +
    262 
    +
    263  // insert into table
    +
    264  if (tbl->num == 0) {
    +
    265  obj->prev = NULL;
    +
    266  obj->next = NULL;
    +
    267  } else {
    +
    268  if (tbl->inserttop == false) {
    +
    269  obj->prev = tbl->last;
    +
    270  obj->next = NULL;
    +
    271  } else {
    +
    272  obj->prev = NULL;
    +
    273  obj->next = tbl->first;
    +
    274  }
    +
    275  }
    +
    276  insertobj(tbl, obj);
    +
    277 
    +
    278  // unlock table
    +
    279  qlisttbl_unlock(tbl);
    +
    280 
    +
    281  return true;
    +
    282 }
    +
    283 
    +
    284 /**
    +
    285  * qlisttbl->putstr(): Put a string into this table.
    +
    286  *
    +
    287  * @param tbl qlisttbl container pointer.
    +
    288  * @param name element name.
    +
    289  * @param str string data.
    +
    290  *
    +
    291  * @return true if successful, otherwise returns false.
    +
    292  * @retval errno will be set in error condition.
    +
    293  * - ENOMEM : Memory allocation failure.
    +
    294  * - EINVAL : Invalid argument.
    +
    295  */
    +
    296 bool qlisttbl_putstr(qlisttbl_t *tbl, const char *name, const char *str)
    +
    297 {
    +
    298  size_t size = (str) ? (strlen(str) + 1) : 0;
    +
    299  return qlisttbl_put(tbl, name, (const void *)str, size);
    +
    300 }
    +
    301 
    +
    302 /**
    +
    303  * qlisttbl->putstrf(): Put a formatted string into this table.
    +
    304  *
    +
    305  * @param tbl qlisttbl container pointer.
    +
    306  * @param name element name.
    +
    307  * @param format formatted value string.
    +
    308  *
    +
    309  * @return true if successful, otherwise returns false.
    +
    310  * @retval errno will be set in error condition.
    +
    311  * - ENOMEM : Memory allocation failure.
    +
    312  * - EINVAL : Invalid argument.
    +
    313  */
    +
    314 bool qlisttbl_putstrf(qlisttbl_t *tbl, const char *name, const char *format, ...)
    +
    315 {
    +
    316  char *str;
    +
    317  DYNAMIC_VSPRINTF(str, format);
    +
    318  if (str == NULL) {
    +
    319  errno = ENOMEM;
    +
    320  return false;
    +
    321  }
    +
    322 
    +
    323  bool ret = qlisttbl_putstr(tbl, name, str);
    +
    324  free(str);
    +
    325 
    +
    326  return ret;
    +
    327 }
    +
    328 
    +
    329 /**
    +
    330  * qlisttbl->putInt(): Put an integer into this table as string type.
    +
    331  *
    +
    332  * @param tbl qlisttbl container pointer.
    +
    333  * @param name element name.
    +
    334  * @param num number data.
    +
    335  *
    +
    336  * @return true if successful, otherwise returns false.
    +
    337  * @retval errno will be set in error condition.
    +
    338  * - ENOMEM : Memory allocation failure.
    +
    339  * - EINVAL : Invalid argument.
    +
    340  *
    +
    341  * @note
    +
    342  * The integer will be converted to a string object and stored as a string
    +
    343  * object.
    +
    344  */
    +
    345 bool qlisttbl_putint(qlisttbl_t *tbl, const char *name, int64_t num)
    +
    346 {
    +
    347  char str[20+1];
    +
    348  snprintf(str, sizeof(str), "%"PRId64, num);
    +
    349  return qlisttbl_putstr(tbl, name, str);
    +
    350 }
    +
    351 
    +
    352 /**
    +
    353  * qlisttbl->get(): Finds an object with given name.
    +
    354  *
    +
    355  * If there are duplicate keys in the table, this will return the first matched
    +
    356  * one from the bottom (or the top if QLISTTBL_LOOKUPFORWARD option was given).
    +
    357  * So if there are duplicated keys, it'll return recently inserted one.
    +
    358  *
    +
    359  * @param tbl qlisttbl container pointer.
    +
    360  * @param name element name.
    +
    361  * @param size if size is not NULL, data size will be stored.
    +
    362  * @param newmem whether or not to allocate memory for the data.
    +
    363  *
    +
    364  * @return a pointer of data if key is found, otherwise returns NULL.
    +
    365  * @retval errno will be set in error condition.
    +
    366  * - ENOENT : No such key found.
    +
    367  * - EINVAL : Invalid argument.
    +
    368  * - ENOMEM : Memory allocation failure.
    +
    369  *
    +
    370  * @code
    +
    371  * qlisttbl_t *tbl = qlisttbl();
    +
    372  * (...codes...)
    +
    373  *
    +
    374  * // with newmem flag unset
    +
    375  * size_t size;
    +
    376  * void *data = tbl->get(tbl, "key_name", &size, false);
    +
    377  *
    +
    378  * // with newmem flag set
    +
    379  * size_t size;
    +
    380  * void *data = tbl->get(tbl, "key_name", &size, true);
    +
    381  * (...does something with data...)
    +
    382  * if(data != NULL) free(data);
    +
    383  * @endcode
    +
    384  *
    +
    385  * @note
    +
    386  * If newmem flag is set, returned data will be malloced and should be
    +
    387  * deallocated by user. Otherwise returned pointer will point internal data
    +
    388  * buffer directly and should not be de-allocated by user. In thread-safe mode,
    +
    389  * always set newmem flag as true to make sure it works in race condition
    +
    390  * situation.
    +
    391  */
    +
    392 void *qlisttbl_get(qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
    +
    393 {
    +
    394  if (name == NULL) {
    +
    395  errno = EINVAL;
    +
    396  return NULL;
    +
    397  }
    +
    398 
    +
    399  qlisttbl_lock(tbl);
    +
    400  void *data = NULL;
    +
    401  qlisttbl_obj_t *obj = findobj(tbl, name, NULL);
    +
    402  if (obj != NULL) {
    +
    403  // get data
    +
    404  if (newmem == true) {
    +
    405  data = malloc(obj->size);
    +
    406  if (data == NULL) {
    +
    407  errno = ENOMEM;
    +
    408  qlisttbl_unlock(tbl);
    +
    409  return NULL;
    +
    410  }
    +
    411  memcpy(data, obj->data, obj->size);
    +
    412  } else {
    +
    413  data = obj->data;
    +
    414  }
    +
    415 
    +
    416  // set size
    +
    417  if (size != NULL) *size = obj->size;
    +
    418  }
    +
    419  qlisttbl_unlock(tbl);
    +
    420 
    +
    421  if (data == NULL) {
    +
    422  errno = ENOENT;
    +
    423  }
    +
    424 
    +
    425  return data;
    +
    426 }
    +
    427 
    +
    428 /**
    +
    429  * qlisttbl->getstr(): Finds an object with given name and returns as
    +
    430  * string type.
    +
    431  *
    +
    432  * @param tbl qlisttbl container pointer.
    +
    433  * @param name element name.
    +
    434  * @param newmem whether or not to allocate memory for the data.
    +
    435  *
    +
    436  * @return a pointer of data if key is found, otherwise returns NULL.
    +
    437  * @retval errno will be set in error condition.
    +
    438  * - ENOENT : No such key found.
    +
    439  * - EINVAL : Invalid argument.
    +
    440  * - ENOMEM : Memory allocation failure.
    +
    441 */
    +
    442 char *qlisttbl_getstr(qlisttbl_t *tbl, const char *name, bool newmem)
    +
    443 {
    +
    444  return (char *)qlisttbl_get(tbl, name, NULL, newmem);
    +
    445 }
    +
    446 
    +
    447 /**
    +
    448  * qlisttbl->getint(): Finds an object with given name and returns as
    +
    449  * integer type.
    +
    450  *
    +
    451  * @param tbl qlisttbl container pointer.
    +
    452  * @param name element name.
    +
    453  *
    +
    454  * @return an integer value of the integer object, otherwise returns 0.
    +
    455  * @retval errno will be set in error condition.
    +
    456  * - ENOENT : No such key found.
    +
    457  * - EINVAL : Invalid argument.
    +
    458  * - ENOMEM : Memory allocation failure.
    +
    459  */
    +
    460 int64_t qlisttbl_getint(qlisttbl_t *tbl, const char *name)
    +
    461 {
    +
    462  int64_t num = 0;
    +
    463  char *str = qlisttbl_getstr(tbl, name, true);
    +
    464  if (str != NULL) {
    +
    465  num = atoll(str);
    +
    466  free(str);
    +
    467  }
    +
    468  return num;
    +
    469 }
    +
    470 
    +
    471 /**
    +
    472  * qlisttbl->getmulti(): Finds all objects with given name and return a array
    +
    473  * of objects.
    +
    474  *
    +
    475  * If there are duplicate keys in the table, this will return all
    +
    476  * the matched ones. The order of objects in return depends on setnextdir()
    +
    477  * setting. By default, the order is same(forward) as listed in the table.
    +
    478  *
    +
    479  * @param tbl qlisttbl container pointer.
    +
    480  * @param name element name.
    +
    481  * @param newmem whether or not to allocate memory for the data.
    +
    482  * @param numobjs the nuber of objects returned will be stored. (can be NULL)
    +
    483  *
    +
    484  * @return a pointer of data if key is found, otherwise returns NULL.
    +
    485  * @retval errno will be set in error condition.
    +
    486  * - ENOENT : No such key found.
    +
    487  * - EINVAL : Invalid argument.
    +
    488  * - ENOMEM : Memory allocation failure.
    +
    489  *
    +
    490  * @note
    +
    491  * The returned array of qlisttbl_data_t should be released by freemulti() call
    +
    492  * after use. Even you call getmulti() with newmem set false, freemulti() should
    +
    493  * be called all the times, so the object array itself can be released.
    +
    494  *
    +
    495  * @code
    +
    496  * size_t numobjs = 0;
    +
    497  * qlisttbl_data_t *objs = tbl->getmulti(tbl, "e2", true, &numobjs);
    +
    498  * for (i = 0; objs[i].data != NULL; i++) {
    +
    499  * printf("DATA=%s, SIZE=%zu\n", i, (char *)objs[i].data, objs[i].size);
    +
    500  * }
    +
    501  * tbl->freemulti(objs);
    +
    502  * @endcode
    +
    503  */
    +
    504 qlisttbl_data_t *qlisttbl_getmulti(qlisttbl_t *tbl, const char *name, bool newmem,
    +
    505  size_t *numobjs)
    +
    506 {
    +
    507  qlisttbl_data_t *objs = NULL; // objects container
    +
    508  size_t allocobjs = 0; // allocated number of objs
    +
    509  size_t numfound = 0; // number of keys found
    +
    510 
    +
    511  qlisttbl_obj_t obj;
    +
    512  memset((void *)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    513  qlisttbl_lock(tbl);
    +
    514  while (tbl->getnext(tbl, &obj, name, newmem) == true) {
    +
    515  numfound++;
    +
    516 
    +
    517  // allocate object array.
    +
    518  if (numfound >= allocobjs) {
    +
    519  if (allocobjs == 0) allocobjs = 10; // start from 10
    +
    520  else allocobjs *= 2; // double size
    +
    521  objs = (qlisttbl_data_t *)realloc(objs, sizeof(qlisttbl_data_t) * allocobjs);
    +
    522  if (objs == NULL) {
    +
    523  DEBUG("qlisttbl->getmulti(): Memory reallocation failure.");
    +
    524  errno = ENOMEM;
    +
    525  break;
    +
    526  }
    +
    527  }
    +
    528 
    +
    529  // copy reference
    +
    530  qlisttbl_data_t *newobj = &objs[numfound - 1];
    +
    531  newobj->data = obj.data;
    +
    532  newobj->size = obj.size;
    +
    533  newobj->type = (newmem == false) ? 1 : 2;
    +
    534 
    +
    535  // release resource
    +
    536  if (newmem == true) {
    +
    537  if (obj.name != NULL) free(obj.name);
    +
    538  }
    +
    539 
    +
    540  // clear next block
    +
    541  newobj = &objs[numfound];
    +
    542  memset((void *)newobj, '\0', sizeof(qlisttbl_data_t));
    +
    543  newobj->type = 0; // mark, end of objects
    +
    544  }
    +
    545  qlisttbl_unlock(tbl);
    +
    546 
    +
    547  // return found counter
    +
    548  if (numobjs != NULL) {
    +
    549  *numobjs = numfound;
    +
    550  }
    +
    551 
    +
    552  if (numfound == 0) {
    +
    553  errno = ENOENT;
    +
    554  }
    +
    555 
    +
    556  return objs;
    +
    557 }
    +
    558 
    +
    559 /**
    +
    560  * qlisttbl->freemulti(): Deallocate object array returned by getmulti().
    +
    561  *
    +
    562  * @param objs pointer of array of qlisttbl_data_t.
    +
    563  *
    +
    564  * @code
    +
    565  * qlisttbl_data_t *objs = tbl->getmulti(tbl, "newmem is true", true, &numobjs);
    +
    566  * tbl->freemulti(objs); // frees allocated objects and object array itself.
    +
    567  *
    +
    568  * qlisttbl_data_t *objs = tbl->getmulti(tbl, "even newmem is false", false, &numobjs);
    +
    569  * tbl->freemulti(objs); // frees object array itself.
    +
    570  *
    +
    571  * @endcode
    +
    572  */
    +
    573 void qlisttbl_freemulti(qlisttbl_data_t *objs)
    +
    574 {
    +
    575  if (objs == NULL) return;
    +
    576 
    +
    577  qlisttbl_data_t *obj;
    +
    578  for (obj = &objs[0]; obj->type == 2; obj++) {
    +
    579  if (obj->data != NULL) free(obj->data);
    +
    580  }
    +
    581 
    +
    582  free(objs);
    +
    583 }
    +
    584 
    +
    585 /**
    +
    586  * qlisttbl->remove(): Remove matched objects with given name.
    +
    587  *
    +
    588  * @param tbl qlisttbl container pointer.
    +
    589  * @param name element name.
    +
    590  *
    +
    591  * @return a number of removed objects.
    +
    592  */
    +
    593 size_t qlisttbl_remove(qlisttbl_t *tbl, const char *name)
    +
    594 {
    +
    595  if (name == NULL) return false;
    +
    596 
    +
    597  size_t numremoved = 0;
    +
    598 
    +
    599  qlisttbl_obj_t obj;
    +
    600  memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    601  qlisttbl_lock(tbl);
    +
    602  while (qlisttbl_getnext(tbl, &obj, name, false) == true) {
    +
    603  qlisttbl_removeobj(tbl, &obj);
    +
    604  numremoved++;
    +
    605  }
    +
    606  qlisttbl_unlock(tbl);
    +
    607 
    +
    608  return numremoved;
    +
    609 }
    +
    610 
    +
    611 /**
    +
    612  * qlisttbl->removeobj(): Remove objects with given object pointer.
    +
    613  *
    +
    614  * This call is useful when you want to remove an element while traversing a
    +
    615  * table using getnext(). So the address pointed by obj maybe different than
    +
    616  * the actual object in a table, but it's ok because we'll recalculate the
    +
    617  * actual object address by referring it's links.
    +
    618  *
    +
    619  * @param tbl qlisttbl container pointer.
    +
    620  * @param name element name.
    +
    621  *
    +
    622  * @return true if succeed on deletion, false if failed.
    +
    623  * @retval errno will be set in error condition.
    +
    624  * - ENOENT : No such key found.
    +
    625  *
    +
    626  * @code
    +
    627  * qlisttbl_obj_t obj;
    +
    628  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    629  * tbl->lock(tbl);
    +
    630  * while(tbl->getnext(tbl, &obj, NULL, true) == true) {
    +
    631  * tbl->removeobj(tbl, &obj); // this will remove all entries in the table
    +
    632  * }
    +
    633  * tbl->unlock(tbl);
    +
    634  * @endcode
    +
    635  */
    +
    636 bool qlisttbl_removeobj(qlisttbl_t *tbl, const qlisttbl_obj_t *obj)
    +
    637 {
    +
    638  if (obj == NULL) return false;
    +
    639 
    +
    640  qlisttbl_lock(tbl);
    +
    641 
    +
    642  // copy chains
    +
    643  qlisttbl_obj_t *prev = obj->prev;
    +
    644  qlisttbl_obj_t *next = obj->next;
    +
    645 
    +
    646  // find this object
    +
    647  qlisttbl_obj_t *this = NULL;
    +
    648  if (prev != NULL) this = prev->next;
    +
    649  else if (next != NULL) this = next->prev;
    +
    650  else this = tbl->first; // table has only one object.
    +
    651 
    +
    652  // double check
    +
    653  if (this == NULL) {
    +
    654  qlisttbl_unlock(tbl);
    +
    655  DEBUG("qlisttbl->removeobj(): Can't veryfy object.");
    +
    656  errno = ENOENT;
    +
    657  return false;
    +
    658  }
    +
    659 
    +
    660  // adjust chain links
    +
    661  if (prev == NULL) tbl->first = next; // if the object is first one
    +
    662  else prev->next = next; // not the first one
    +
    663 
    +
    664  if (next == NULL) tbl->last = prev; // if the object is last one
    +
    665  else next->prev = prev; // not the first one
    +
    666 
    +
    667  // adjust counter
    +
    668  tbl->num--;
    +
    669 
    +
    670  qlisttbl_unlock(tbl);
    +
    671 
    +
    672  // free object
    +
    673  free(this->name);
    +
    674  free(this->data);
    +
    675  free(this);
    +
    676 
    +
    677  return true;
    +
    678 }
    +
    679 
    +
    680 /**
    +
    681  * qlisttbl->getnext(): Get next element.
    +
    682  *
    +
    683  * Default searching direction is backward, from the bottom to top
    +
    684  * unless QLISTTBL_LOOKUPFORWARD option was specified.
    +
    685  *
    +
    686  * @param tbl qlisttbl container pointer.
    +
    687  * @param obj found data will be stored in this object
    +
    688  * @param name element name., if key name is NULL search every objects in
    +
    689  * the table.
    +
    690  * @param newmem whether or not to allocate memory for the data.
    +
    691  *
    +
    692  * @return true if found otherwise returns false
    +
    693  * @retval errno will be set in error condition.
    +
    694  * - ENOENT : No next element.
    +
    695  * - ENOMEM : Memory allocation failure.
    +
    696  *
    +
    697  * @note
    +
    698  * The obj should be initialized with 0 by using memset() before first call.
    +
    699  * If newmem flag is true, user should de-allocate obj.name and obj.data
    +
    700  * resources.
    +
    701  *
    +
    702  * @code
    +
    703  * qlisttbl_t *tbl = qlisttbl();
    +
    704  * (...add data into table...)
    +
    705  *
    +
    706  * // non-thread usages
    +
    707  * qlisttbl_obj_t obj;
    +
    708  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    709  * while(tbl->getnext(tbl, &obj, NULL, false) == true) {
    +
    710  * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    +
    711  * obj.name, (char*)obj.data, obj.size);
    +
    712  * }
    +
    713  *
    +
    714  * // thread model with particular key search
    +
    715  * qlisttbl_obj_t obj;
    +
    716  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    717  * tbl->lock(tbl);
    +
    718  * while(tbl->getnext(tbl, &obj, "key_name", true) == true) {
    +
    719  * printf("NAME=%s, DATA=%s, SIZE=%zu\n",
    +
    720  * obj.name, (char*)obj.data, obj.size);
    +
    721  * free(obj.name);
    +
    722  * free(obj.data);
    +
    723  * }
    +
    724  * tbl->unlock(tbl);
    +
    725  * @endcode
    +
    726  */
    +
    727 bool qlisttbl_getnext(qlisttbl_t *tbl, qlisttbl_obj_t *obj, const char *name,
    +
    728  bool newmem)
    +
    729 {
    +
    730  if (obj == NULL) return NULL;
    +
    731 
    +
    732  qlisttbl_lock(tbl);
    +
    733 
    +
    734  qlisttbl_obj_t *cont = NULL;
    +
    735  if (obj->size == 0) { // first time call
    +
    736  if (name == NULL) { // full scan
    +
    737  cont = (tbl->lookupforward) ? tbl->first : tbl->last;
    +
    738  } else { // name search
    +
    739  cont = findobj(tbl, name, NULL);
    +
    740  }
    +
    741  } else { // next call
    +
    742  cont = (tbl->lookupforward) ? obj->next : obj->prev;
    +
    743  }
    +
    744 
    +
    745  if (cont == NULL) {
    +
    746  errno = ENOENT;
    +
    747  qlisttbl_unlock(tbl);
    +
    748  return false;
    +
    749  }
    +
    750 
    +
    751  uint32_t hash = (name != NULL) ? qhashmurmur3_32(name, strlen(name)) : 0;
    +
    752 
    +
    753  bool ret = false;
    +
    754  while (cont != NULL) {
    +
    755  if (name == NULL || tbl->namematch(cont, name, hash) == true) {
    +
    756  if (newmem == true) {
    +
    757  obj->name = strdup(cont->name);
    +
    758  obj->data = malloc(cont->size);
    +
    759  if (obj->name == NULL || obj->data == NULL) {
    +
    760  if (obj->name != NULL) free(obj->name);
    +
    761  if (obj->data != NULL) free(obj->data);
    +
    762  obj->name = NULL;
    +
    763  obj->data = NULL;
    +
    764  errno = ENOMEM;
    +
    765  break;
    +
    766  }
    +
    767  memcpy(obj->data, cont->data, cont->size);
    +
    768  } else {
    +
    769  obj->name = cont->name;
    +
    770  obj->data = cont->data;
    +
    771  }
    +
    772  obj->hash = cont->hash;
    +
    773  obj->size = cont->size;
    +
    774  obj->prev = cont->prev;
    +
    775  obj->next = cont->next;
    +
    776 
    +
    777  ret = true;
    +
    778  break;
    +
    779  }
    +
    780 
    +
    781  cont = (tbl->lookupforward) ? cont->next : cont->prev;
    +
    782  }
    +
    783  qlisttbl_unlock(tbl);
    +
    784 
    +
    785  if (ret == false) {
    +
    786  errno = ENOENT;
    +
    787  }
    +
    788 
    +
    789  return ret;
    +
    790 }
    +
    791 
    +
    792 /**
    +
    793  * qlisttbl->size(): Returns the number of elements in this table.
    +
    794  *
    +
    795  * @param tbl qlisttbl container pointer.
    +
    796  *
    +
    797  * @return the number of elements in this table.
    +
    798  */
    +
    799 size_t qlisttbl_size(qlisttbl_t *tbl)
    +
    800 {
    +
    801  return tbl->num;
    +
    802 }
    +
    803 
    +
    804 /**
    +
    805  * qlisttbl->sort(): Sort keys in this table.
    +
    806  *
    +
    807  * @param tbl qlisttbl container pointer.
    +
    808  *
    +
    809  * @note
    +
    810  * It will sort the table in ascending manner, if you need descending order somehow,
    +
    811  * lookup-backword option will do the job without changing the order in the table.
    +
    812  *
    +
    813  * @code
    +
    814  * The appearence order of duplicated keys will be preserved in a sored table.
    +
    815  * See how duplicated key 'b' is ordered in below example
    +
    816  *
    +
    817  * [Unsorted] [sorted ASC] [sorted DES]
    +
    818  * d = 1 a = 2 d = 1
    +
    819  * a = 2 b = 3 c = 5
    +
    820  * b = 3 b = 4 b = 3
    +
    821  * b = 4 b = 6 b = 4
    +
    822  * c = 5 c = 5 b = 6
    +
    823  * b = 6 d = 1 a = 2
    +
    824  * @endcode
    +
    825  */
    +
    826 void qlisttbl_sort(qlisttbl_t *tbl)
    +
    827 {
    +
    828  // run bubble sort
    +
    829  qlisttbl_lock(tbl);
    +
    830  qlisttbl_obj_t *obj1, *obj2;
    +
    831  qlisttbl_obj_t tmpobj;
    +
    832  int n, n2, i;
    +
    833  for (n = tbl->num; n > 0;) {
    +
    834  n2 = 0;
    +
    835  for (i = 0, obj1 = tbl->first; i < (n - 1); i++, obj1 = obj1->next) {
    +
    836  obj2 = obj1->next; // this can't be null.
    +
    837  if (tbl->namecmp(obj1->name, obj2->name) > 0) {
    +
    838  // swapping contents is faster than adjusting links.
    +
    839  tmpobj = *obj1;
    +
    840  obj1->hash = obj2->hash;
    +
    841  obj1->name = obj2->name;
    +
    842  obj1->data = obj2->data;
    +
    843  obj1->size = obj2->size;
    +
    844  obj2->hash = tmpobj.hash;
    +
    845  obj2->name = tmpobj.name;
    +
    846  obj2->data = tmpobj.data;
    +
    847  obj2->size = tmpobj.size;
    +
    848 
    +
    849  n2 = i + 1;
    +
    850  }
    +
    851  }
    +
    852  n = n2; // skip sorted tailing elements
    +
    853  }
    +
    854  qlisttbl_unlock(tbl);
    +
    855 }
    +
    856 
    +
    857 /**
    +
    858  * qlisttbl->clear(): Removes all of the elements from this table.
    +
    859  *
    +
    860  * @param tbl qlisttbl container pointer.
    +
    861  */
    +
    862 void qlisttbl_clear(qlisttbl_t *tbl)
    +
    863 {
    +
    864  qlisttbl_lock(tbl);
    +
    865  qlisttbl_obj_t *obj;
    +
    866  for (obj = tbl->first; obj != NULL;) {
    +
    867  qlisttbl_obj_t *next = obj->next;
    +
    868  free(obj->name);
    +
    869  free(obj->data);
    +
    870  free(obj);
    +
    871  obj = next;
    +
    872  }
    +
    873 
    +
    874  tbl->num = 0;
    +
    875  tbl->first = NULL;
    +
    876  tbl->last = NULL;
    +
    877  qlisttbl_unlock(tbl);
    +
    878 }
    +
    879 
    +
    880 /**
    +
    881  * qlisttbl->save(): Save qlisttbl as plain text format
    +
    882  * Dumping direction is always forward(from the top to the bottom) to preserve
    +
    883  * the order when we load the file again.
    +
    884  *
    +
    885  * @param tbl qlisttbl container pointer.
    +
    886  * @param filepath save file path
    +
    887  * @param sepchar separator character between name and value. normally '=' is
    +
    888  * used.
    +
    889  * @param encode flag for encoding data . false can be used if the all data
    +
    890  * are string or integer type and has no new line. otherwise
    +
    891  * true must be set.
    +
    892  *
    +
    893  * @return true if successful, otherwise returns false.
    +
    894  */
    +
    895 bool qlisttbl_save(qlisttbl_t *tbl, const char *filepath, char sepchar,
    +
    896  bool encode)
    +
    897 {
    +
    898  if (filepath == NULL) {
    +
    899  errno = EINVAL;
    +
    900  return false;
    +
    901  }
    +
    902 
    +
    903  int fd;
    +
    904  if ((fd = open(filepath, O_CREAT|O_WRONLY|O_TRUNC, (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))) < 0) {
    +
    905  DEBUG("qlisttbl->save(): Can't open file %s", filepath);
    +
    906  return false;
    +
    907  }
    +
    908 
    +
    909  char *gmtstr = qtime_gmt_str(0);
    +
    910  qio_printf(fd, -1, "# %s %s\n", filepath, gmtstr);
    +
    911  free(gmtstr);
    +
    912 
    +
    913  qlisttbl_lock(tbl);
    +
    914  qlisttbl_obj_t *obj;
    +
    915  for (obj = tbl->first; obj; obj = obj->next) {
    +
    916  char *encval;
    +
    917  if (encode == true) encval = qurl_encode(obj->data, obj->size);
    +
    918  else encval = obj->data;
    +
    919  qio_printf(fd, -1, "%s%c%s\n", obj->name, sepchar, encval);
    +
    920  if (encode == true) free(encval);
    +
    921  }
    +
    922  qlisttbl_unlock(tbl);
    +
    923 
    +
    924  close(fd);
    +
    925  return true;
    +
    926 }
    +
    927 
    +
    928 /**
    +
    929  * qlisttbl->load(): Load and append entries from given filepath
    +
    930  * Loading direction is always appending at the bottom of the table to preserve
    +
    931  * the order as it was.
    +
    932  *
    +
    933  * @param tbl qlisttbl container pointer.
    +
    934  * @param filepath save file path
    +
    935  * @param sepchar separator character between name and value. normally '=' is
    +
    936  * used
    +
    937  * @param decode flag for decoding data
    +
    938  *
    +
    939  * @return the number of loaded entries, otherwise returns -1.
    +
    940  */
    +
    941 ssize_t qlisttbl_load(qlisttbl_t *tbl, const char *filepath, char sepchar,
    +
    942  bool decode)
    +
    943 {
    +
    944  // load file
    +
    945  char *str = qfile_load(filepath, NULL);
    +
    946  if (str == NULL) return -1;
    +
    947 
    +
    948  // parse
    +
    949  qlisttbl_lock(tbl);
    +
    950  char *offset, *buf;
    +
    951  int cnt = 0;
    +
    952  for (offset = str; *offset != '\0'; ) {
    +
    953  // get one line into buf
    +
    954  for (buf = offset; *offset != '\n' && *offset != '\0'; offset++);
    +
    955  if (*offset != '\0') {
    +
    956  *offset = '\0';
    +
    957  offset++;
    +
    958  }
    +
    959  qstrtrim(buf);
    +
    960 
    +
    961  // skip blank or comment line
    +
    962  if ((buf[0] == '#') || (buf[0] == '\0')) continue;
    +
    963 
    +
    964  // parse
    +
    965  char *data = strdup(buf);
    +
    966  char *name = _q_makeword(data, sepchar);
    +
    967  qstrtrim(data);
    +
    968  qstrtrim(name);
    +
    969  if (decode == true) qurl_decode(data);
    +
    970 
    +
    971  // add to the table.
    +
    972  qlisttbl_put(tbl, name, data, strlen(data) + 1);
    +
    973 
    +
    974  free(name);
    +
    975  free(data);
    +
    976  }
    +
    977  qlisttbl_unlock(tbl);
    +
    978  free(str);
    +
    979 
    +
    980  return cnt;
    +
    981 }
    +
    982 
    +
    983 /**
    +
    984  * qlisttbl->debug(): Print out stored elements for debugging purpose.
    +
    985  *
    +
    986  * @param tbl qlisttbl container pointer.
    +
    987  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    988  *
    +
    989  * @return true if successful, otherwise returns false.
    +
    990  * @retval errno will be set in error condition.
    +
    991  * - EIO : Invalid output stream.
    +
    992  */
    +
    993 bool qlisttbl_debug(qlisttbl_t *tbl, FILE *out)
    +
    994 {
    +
    995  if (out == NULL) {
    +
    996  errno = EIO;
    +
    997  return false;
    +
    998  }
    +
    999 
    +
    1000  qlisttbl_lock(tbl);
    +
    1001  qlisttbl_obj_t *obj;
    +
    1002  for (obj = tbl->first; obj; obj = obj->next) {
    +
    1003  fprintf(out, "%s=" , obj->name);
    +
    1004  _q_textout(out, obj->data, obj->size, MAX_HUMANOUT);
    +
    1005  fprintf(out, " (%zu, %08x)\n" , obj->size, obj->hash);
    +
    1006  }
    +
    1007  qlisttbl_unlock(tbl);
    +
    1008 
    +
    1009  return true;
    +
    1010 }
    +
    1011 
    +
    1012 /**
    +
    1013  * qlisttbl->lock(): Enter critical section.
    +
    1014  *
    +
    1015  * @param tbl qlisttbl container pointer.
    +
    1016  *
    +
    1017  * @note
    +
    1018  * Normally explicit locking is only needed when traverse all the
    +
    1019  * elements with qlisttbl->getnext().
    +
    1020  */
    +
    1021 void qlisttbl_lock(qlisttbl_t *tbl)
    +
    1022 {
    +
    1023  Q_MUTEX_ENTER(tbl->qmutex);
    +
    1024 }
    +
    1025 
    +
    1026 /**
    +
    1027  * qlisttbl->unlock(): Leave critical section.
    +
    1028  *
    +
    1029  * @param tbl qlisttbl container pointer.
    +
    1030  */
    +
    1031 void qlisttbl_unlock(qlisttbl_t *tbl)
    +
    1032 {
    +
    1033  Q_MUTEX_LEAVE(tbl->qmutex);
    +
    1034 }
    +
    1035 
    +
    1036 /**
    +
    1037  * qlisttbl->free(): Free qlisttbl_t
    +
    1038  *
    +
    1039  * @param tbl qlisttbl container pointer.
    +
    1040  */
    +
    1041 void qlisttbl_free(qlisttbl_t *tbl)
    +
    1042 {
    +
    1043  qlisttbl_clear(tbl);
    +
    1044  Q_MUTEX_DESTROY(tbl->qmutex);
    +
    1045  free(tbl);
    +
    1046 }
    +
    1047 
    +
    1048 #ifndef _DOXYGEN_SKIP
    +
    1049 
    +
    1050 // lock must be obtained from caller
    +
    1051 static qlisttbl_obj_t *newobj(const char *name, const void *data, size_t size)
    +
    1052 {
    +
    1053  if (name == NULL || data == NULL || size <= 0) {
    +
    1054  errno = EINVAL;
    +
    1055  return false;
    +
    1056  }
    +
    1057 
    +
    1058  // make a new object
    +
    1059  char *dup_name = strdup(name);
    +
    1060  void *dup_data = malloc(size);
    +
    1061  qlisttbl_obj_t *obj = (qlisttbl_obj_t *)malloc(sizeof(qlisttbl_obj_t));
    +
    1062  if (dup_name == NULL || dup_data == NULL || obj == NULL) {
    +
    1063  if (dup_name != NULL) free(dup_name);
    +
    1064  if (dup_data != NULL) free(dup_data);
    +
    1065  if (obj != NULL) free(obj);
    +
    1066  errno = ENOMEM;
    +
    1067  return NULL;
    +
    1068  }
    +
    1069  memcpy(dup_data, data, size);
    +
    1070  memset((void *)obj, '\0', sizeof(qlisttbl_obj_t));
    +
    1071 
    +
    1072  // obj->hash = qhashmurmur3_32(dup_name);
    +
    1073  obj->name = dup_name;
    +
    1074  obj->data = dup_data;
    +
    1075  obj->size = size;
    +
    1076 
    +
    1077  return obj;
    +
    1078 }
    +
    1079 
    +
    1080 // lock must be obtained from caller
    +
    1081 static bool insertobj(qlisttbl_t *tbl, qlisttbl_obj_t *obj)
    +
    1082 {
    +
    1083  // update hash
    +
    1084  obj->hash = qhashmurmur3_32(obj->name, strlen(obj->name));
    +
    1085 
    +
    1086  qlisttbl_obj_t *prev = obj->prev;
    +
    1087  qlisttbl_obj_t *next = obj->next;
    +
    1088 
    +
    1089  if (prev == NULL) tbl->first = obj;
    +
    1090  else prev->next = obj;
    +
    1091 
    +
    1092  if (next == NULL) tbl->last = obj;
    +
    1093  else next->prev = obj;
    +
    1094 
    +
    1095  // increase counter
    +
    1096  tbl->num++;
    +
    1097 
    +
    1098  return true;
    +
    1099 }
    +
    1100 
    +
    1101 // lock must be obtained from caller
    +
    1102 static qlisttbl_obj_t *findobj(qlisttbl_t *tbl, const char *name, qlisttbl_obj_t *retobj)
    +
    1103 {
    +
    1104  if (retobj != NULL) {
    +
    1105  memset((void *)retobj, '\0', sizeof(qlisttbl_obj_t));
    +
    1106  }
    +
    1107 
    +
    1108  if (name == NULL || tbl->num == 0) {
    +
    1109  errno = ENOENT;
    +
    1110  return NULL;
    +
    1111  }
    +
    1112 
    +
    1113  uint32_t hash = qhashmurmur3_32(name, strlen(name));
    +
    1114  qlisttbl_obj_t *obj = (tbl->lookupforward) ? tbl->first : tbl->last;
    +
    1115  while (obj != NULL) {
    +
    1116  // name string will be compared only if the hash matches.
    +
    1117  if (tbl->namematch(obj, name, hash) == true) {
    +
    1118  if (retobj != NULL) {
    +
    1119  *retobj = *obj;
    +
    1120  }
    +
    1121  return obj;
    +
    1122  }
    +
    1123  obj = (tbl->lookupforward)? obj->next : obj->prev;
    +
    1124  }
    +
    1125 
    +
    1126  // not found, set prev and next chain.
    +
    1127  if (retobj != NULL) {
    +
    1128  if (tbl->inserttop) {
    +
    1129  retobj->prev = NULL;
    +
    1130  retobj->next = tbl->first;
    +
    1131  } else {
    +
    1132  retobj->prev = tbl->last;
    +
    1133  retobj->next = NULL;
    +
    1134  }
    +
    1135  }
    +
    1136  errno = ENOENT;
    +
    1137 
    +
    1138  return NULL;
    +
    1139 }
    +
    1140 
    +
    1141 // key comp
    +
    1142 static bool namematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash)
    +
    1143 {
    +
    1144  if ((obj->hash == hash) && !strcmp(obj->name, name)) {
    +
    1145  return true;
    +
    1146  }
    +
    1147  return false;
    +
    1148 }
    +
    1149 
    +
    1150 static bool namecasematch(qlisttbl_obj_t *obj, const char *name, uint32_t hash)
    +
    1151 {
    +
    1152  if (!strcasecmp(obj->name, name)) {
    +
    1153  return true;
    +
    1154  }
    +
    1155  return false;
    +
    1156 }
    +
    1157 
    +
    1158 #endif /* _DOXYGEN_SKIP */
    +
    char * qurl_encode(const void *bin, size_t size)
    Encode data using URL encoding(Percent encoding) algorithm.
    Definition: qencode.c:125
    +
    size_t qurl_decode(char *str)
    Decode URL encoded string.
    Definition: qencode.c:192
    +
    void * qfile_load(const char *filepath, size_t *nbytes)
    Load file into memory.
    Definition: qfile.c:159
    +
    uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
    Get 32-bit Murmur3 hash.
    Definition: qhash.c:263
    +
    ssize_t qio_printf(int fd, int timeoutms, const char *format,...)
    Formatted output to a file descriptor.
    Definition: qio.c:325
    +
    bool qlisttbl_put(qlisttbl_t *tbl, const char *name, const void *data, size_t size)
    qlisttbl->put(): Put an element to this table.
    Definition: qlisttbl.c:249
    +
    bool qlisttbl_putstrf(qlisttbl_t *tbl, const char *name, const char *format,...)
    qlisttbl->putstrf(): Put a formatted string into this table.
    Definition: qlisttbl.c:314
    +
    void qlisttbl_sort(qlisttbl_t *tbl)
    qlisttbl->sort(): Sort keys in this table.
    Definition: qlisttbl.c:826
    +
    bool qlisttbl_putint(qlisttbl_t *tbl, const char *name, int64_t num)
    qlisttbl->putInt(): Put an integer into this table as string type.
    Definition: qlisttbl.c:345
    +
    void qlisttbl_freemulti(qlisttbl_data_t *objs)
    qlisttbl->freemulti(): Deallocate object array returned by getmulti().
    Definition: qlisttbl.c:573
    +
    bool qlisttbl_removeobj(qlisttbl_t *tbl, const qlisttbl_obj_t *obj)
    qlisttbl->removeobj(): Remove objects with given object pointer.
    Definition: qlisttbl.c:636
    +
    size_t qlisttbl_size(qlisttbl_t *tbl)
    qlisttbl->size(): Returns the number of elements in this table.
    Definition: qlisttbl.c:799
    +
    void qlisttbl_free(qlisttbl_t *tbl)
    qlisttbl->free(): Free qlisttbl_t
    Definition: qlisttbl.c:1041
    +
    void qlisttbl_lock(qlisttbl_t *tbl)
    qlisttbl->lock(): Enter critical section.
    Definition: qlisttbl.c:1021
    +
    void qlisttbl_unlock(qlisttbl_t *tbl)
    qlisttbl->unlock(): Leave critical section.
    Definition: qlisttbl.c:1031
    +
    char * qlisttbl_getstr(qlisttbl_t *tbl, const char *name, bool newmem)
    qlisttbl->getstr(): Finds an object with given name and returns as string type.
    Definition: qlisttbl.c:442
    +
    bool qlisttbl_putstr(qlisttbl_t *tbl, const char *name, const char *str)
    qlisttbl->putstr(): Put a string into this table.
    Definition: qlisttbl.c:296
    +
    void qlisttbl_clear(qlisttbl_t *tbl)
    qlisttbl->clear(): Removes all of the elements from this table.
    Definition: qlisttbl.c:862
    +
    bool qlisttbl_save(qlisttbl_t *tbl, const char *filepath, char sepchar, bool encode)
    qlisttbl->save(): Save qlisttbl as plain text format Dumping direction is always forward(from the top...
    Definition: qlisttbl.c:895
    +
    bool qlisttbl_debug(qlisttbl_t *tbl, FILE *out)
    qlisttbl->debug(): Print out stored elements for debugging purpose.
    Definition: qlisttbl.c:993
    +
    bool qlisttbl_getnext(qlisttbl_t *tbl, qlisttbl_obj_t *obj, const char *name, bool newmem)
    qlisttbl->getnext(): Get next element.
    Definition: qlisttbl.c:727
    +
    size_t qlisttbl_remove(qlisttbl_t *tbl, const char *name)
    qlisttbl->remove(): Remove matched objects with given name.
    Definition: qlisttbl.c:593
    +
    qlisttbl_t * qlisttbl(int options)
    Create a new Q_LIST linked-list container.
    Definition: qlisttbl.c:150
    +
    ssize_t qlisttbl_load(qlisttbl_t *tbl, const char *filepath, char sepchar, bool decode)
    qlisttbl->load(): Load and append entries from given filepath Loading direction is always appending a...
    Definition: qlisttbl.c:941
    +
    int64_t qlisttbl_getint(qlisttbl_t *tbl, const char *name)
    qlisttbl->getint(): Finds an object with given name and returns as integer type.
    Definition: qlisttbl.c:460
    +
    qlisttbl_data_t * qlisttbl_getmulti(qlisttbl_t *tbl, const char *name, bool newmem, size_t *numobjs)
    qlisttbl->getmulti(): Finds all objects with given name and return a array of objects.
    Definition: qlisttbl.c:504
    +
    void * qlisttbl_get(qlisttbl_t *tbl, const char *name, size_t *size, bool newmem)
    qlisttbl->get(): Finds an object with given name.
    Definition: qlisttbl.c:392
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    +
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition: qtime.c:174
    diff --git a/doc/html/qlog_8c.html b/doc/html/qlog_8c.html index f4cb0ad3..82bdcd45 100644 --- a/doc/html/qlog_8c.html +++ b/doc/html/qlog_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qlog.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qlog.c File Reference
    +
    +
    qlog.c File Reference
    @@ -70,32 +70,32 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - +

    +

    Functions

    qlog_t * qlog (const char *filepathfmt, mode_t mode, int rotateinterval, int options)
     Open ratating-log file.
     
    qlog_t * qlog (const char *filepathfmt, mode_t mode, int rotateinterval, int options)
     Open ratating-log file. More...
     
    static bool write_ (qlog_t *log, const char *str)
     qlog->write(): Log messages
     qlog->write(): Log messages More...
     
    static bool writef (qlog_t *log, const char *format,...)
     qlog->writef(): Log messages
     qlog->writef(): Log messages More...
     
    static bool duplicate (qlog_t *log, FILE *outfp, bool flush)
     qlog->duplicate(): Duplicate log string into other stream
     qlog->duplicate(): Duplicate log string into other stream More...
     
    static bool flush_ (qlog_t *log)
     qlog->flush(): Flush buffered log
     qlog->flush(): Flush buffered log More...
     
    static void free_ (qlog_t *log)
     qlog->free(): Close ratating-log file & de-allocate resources
     qlog->free(): Close ratating-log file & de-allocate resources More...
     

    Detailed Description

    Rotating file logger object.

    qlog implements a auto-rotating file logger.

    // create a daily-rotating log file.
    -
    qlog_t *log = qlog("/tmp/dailylog-%Y%m%d.err", 0644, 86400, false);
    +
    qlog_t *log = qlog("/tmp/dailylog-%Y%m%d.err", 0644, 86400, false);
    if(log == NULL) return;
    // screen out.
    @@ -107,18 +107,18 @@
    // close and release resources.
    log->free(log);
    -
    qlog_t * qlog(const char *filepathfmt, mode_t mode, int rotateinterval, int options)
    Open ratating-log file.
    Definition qlog.c:105
    +
    qlog_t * qlog(const char *filepathfmt, mode_t mode, int rotateinterval, int options)
    Open ratating-log file.
    Definition: qlog.c:105

    Definition in file qlog.c.

    Function Documentation

    - -

    ◆ qlog()

    + +

    ◆ qlog()

    - + @@ -167,15 +167,15 @@

    qlog_t *log = qlog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, QLOG_OPT_THREADSAFE);
    +
    qlog_t *log = qlog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, QLOG_OPT_THREADSAFE);
    log->free(log);

    Definition at line 105 of file qlog.c.

    - -

    ◆ write_()

    + +

    ◆ write_()

    @@ -222,8 +222,8 @@

    -

    ◆ writef()

    + +

    ◆ writef()

    @@ -276,8 +276,8 @@

    -

    ◆ duplicate()

    + +

    ◆ duplicate()

    @@ -334,8 +334,8 @@

    -

    ◆ flush_()

    + +

    ◆ flush_()

    @@ -371,8 +371,8 @@

    -

    ◆ free_()

    + +

    ◆ free_()

    @@ -413,7 +413,7 @@

    diff --git a/doc/html/qlog_8c.js b/doc/html/qlog_8c.js index a7d53a2a..af46cc5a 100644 --- a/doc/html/qlog_8c.js +++ b/doc/html/qlog_8c.js @@ -1,6 +1,6 @@ var qlog_8c = [ - [ "qlog", "qlog_8c.html#ad58c2d6cf7daa2976934150e69c83498", null ], + [ "qlog", "qlog_8c.html#acfc1a51cddb0a4e8555ea66f42276874", null ], [ "write_", "qlog_8c.html#a1d9678a766dbbe0cf4863296588a70ce", null ], [ "writef", "qlog_8c.html#a0f1c08e4e0616731b8c5be87a8752f72", null ], [ "duplicate", "qlog_8c.html#a3c66d4096cbaac34db4659e04a0d6335", null ], diff --git a/doc/html/qlog_8c_source.html b/doc/html/qlog_8c_source.html index 3931871b..7234b3d5 100644 --- a/doc/html/qlog_8c_source.html +++ b/doc/html/qlog_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qlog.c Source File @@ -20,8 +20,8 @@

    qlog_t * qlog qlog_t* qlog ( const char *  filepathfmt,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,368 +52,369 @@
    -
    qlog.c
    +
    +
    qlog.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qlog.c Rotating file logger object.
    -
    31 *
    -
    32 * qlog implements a auto-rotating file logger.
    -
    33 *
    -
    34 * @code
    -
    35 * // create a daily-rotating log file.
    -
    36 * qlog_t *log = qlog("/tmp/dailylog-%Y%m%d.err", 0644, 86400, false);
    -
    37 * if(log == NULL) return;
    -
    38 *
    -
    39 * // screen out.
    -
    40 * log->duplicate(log, stdout, true);
    -
    41 *
    -
    42 * // write logs.
    -
    43 * log->write(log, "Service started.");
    -
    44 * log->writef(log, "Server Id: %d", 1);
    -
    45 *
    -
    46 * // close and release resources.
    -
    47 * log->free(log);
    -
    48 * @endcode
    -
    49 */
    -
    50
    -
    51#ifndef DISABLE_QLOG
    -
    52
    -
    53#include <stdio.h>
    -
    54#include <stdlib.h>
    -
    55#include <stdbool.h>
    -
    56#include <string.h>
    -
    57#include <stdarg.h>
    -
    58#include <unistd.h>
    -
    59#include <time.h>
    -
    60#include <sys/stat.h>
    -
    61#include <errno.h>
    -
    62#include "qinternal.h"
    -
    63#include "utilities/qstring.h"
    -
    64#include "extensions/qlog.h"
    -
    65
    -
    66#ifndef _DOXYGEN_SKIP
    -
    67static bool write_(qlog_t *log, const char *str);
    -
    68static bool writef(qlog_t *log, const char *format, ...);
    -
    69static bool duplicate(qlog_t *log, FILE *outfp, bool flush);
    -
    70static bool flush_(qlog_t *log);
    -
    71static void free_(qlog_t *log);
    -
    72
    -
    73// internal usages
    -
    74static bool _real_open(qlog_t *log);
    -
    75#endif
    -
    76
    -
    77/**
    -
    78 * Open ratating-log file
    -
    79 *
    -
    80 * @param filepathfmt filename format. formatting argument is same as
    -
    81 * strftime()
    -
    82 * @param mode new file mode. 0 for system default
    -
    83 * @param rotateinterval rotating interval seconds, set 0 to disable rotation
    -
    84 * @param options combination of options.
    -
    85 *
    -
    86 * @return a pointer of qlog_t structure
    -
    87 *
    -
    88 * @note
    -
    89 * rotateinterval is not relative time. If you set it to 3600, log file will be
    -
    90 * rotated at every hour. And filenameformat is same as strftime(). So If you
    -
    91 * want to log with hourly rotating, filenameformat must be defined including
    -
    92 * hour format like "/somepath/xxx-%Y%m%d%H.log". You can set it to
    -
    93 * "/somepath/xxx-%H.log" for daily overrided log file.
    -
    94 *
    -
    95 * @note
    -
    96 * Available options:
    -
    97 * - QLOG_OPT_THREADSAFE - make it thread-safe.
    -
    98 * - QLOG_OPT_FLUSH - flush out buffer everytime.
    -
    99 *
    -
    100 * @code
    -
    101 * qlog_t *log = qlog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, QLOG_OPT_THREADSAFE);
    -
    102 * log->free(log);
    -
    103 * @endcode
    -
    104 */
    -
    105qlog_t *qlog(const char *filepathfmt, mode_t mode, int rotateinterval,
    -
    106 int options) {
    -
    107 qlog_t *log;
    -
    108
    -
    109 // malloc qlog_t structure
    -
    110 log = (qlog_t *) calloc(1, sizeof(qlog_t));
    -
    111 if (log == NULL) {
    -
    112 errno = ENOMEM;
    -
    113 return NULL;
    -
    114 }
    -
    115
    -
    116 // set up the structure.
    -
    117 qstrcpy(log->filepathfmt, sizeof(log->filepathfmt), filepathfmt);
    -
    118 log->mode = mode;
    -
    119 if (rotateinterval > 0)
    -
    120 log->rotateinterval = rotateinterval;
    -
    121
    -
    122 // handle options
    -
    123 if (options & QLOG_OPT_THREADSAFE) {
    -
    124 Q_MUTEX_NEW(log->qmutex, true);
    -
    125 if (log->qmutex == NULL) {
    -
    126 errno = ENOMEM;
    -
    127 free(log);
    -
    128 return NULL;
    -
    129 }
    -
    130 }
    -
    131 if (options & QLOG_OPT_FLUSH) {
    -
    132 log->logflush = true;
    -
    133 }
    -
    134
    -
    135 // try to open the log file.
    -
    136 if (_real_open(log) == false) {
    -
    137 Q_MUTEX_DESTROY(log->qmutex);
    -
    138 free(log);
    -
    139 return NULL;
    -
    140 }
    -
    141
    -
    142 // member methods
    -
    143 log->write = write_;
    -
    144 log->writef = writef;
    -
    145 log->duplicate = duplicate;
    -
    146 log->flush = flush_;
    -
    147 log->free = free_;
    -
    148
    -
    149 return log;
    -
    150}
    -
    151
    -
    152/**
    -
    153 * qlog->write(): Log messages
    -
    154 *
    -
    155 * @param log a pointer of qlog_t
    -
    156 * @param str message string
    -
    157 *
    -
    158 * @return true if successful, otherewise returns false
    -
    159 */
    -
    160static bool write_(qlog_t *log, const char *str) {
    -
    161 if (log == NULL || log->fp == NULL)
    -
    162 return false;
    -
    163
    -
    164 Q_MUTEX_ENTER(log->qmutex);
    -
    165
    -
    166 /* duplicate stream */
    -
    167 if (log->outfp != NULL) {
    -
    168 fprintf(log->outfp, "%s\n", str);
    -
    169 if (log->outflush == true)
    -
    170 fflush(log->outfp);
    -
    171 }
    -
    172
    -
    173 /* check if log rotation is needed */
    -
    174 if (log->nextrotate > 0 && time(NULL) >= log->nextrotate) {
    -
    175 _real_open(log);
    -
    176 }
    -
    177
    -
    178 /* log to file */
    -
    179 bool ret = false;
    -
    180 if (fprintf(log->fp, "%s\n", str) >= 0) {
    -
    181 if (log->logflush == true)
    -
    182 fflush(log->fp);
    -
    183 ret = true;
    -
    184 }
    -
    185
    -
    186 Q_MUTEX_LEAVE(log->qmutex);
    -
    187
    -
    188 return ret;
    -
    189}
    -
    190
    -
    191/**
    -
    192 * qlog->writef(): Log messages
    -
    193 *
    -
    194 * @param log a pointer of qlog_t
    -
    195 * @param format messages format
    -
    196 *
    -
    197 * @return true if successful, otherewise returns false
    -
    198 */
    -
    199static bool writef(qlog_t *log, const char *format, ...) {
    -
    200 if (log == NULL || log->fp == NULL)
    -
    201 return false;
    -
    202
    -
    203 char *str;
    -
    204 DYNAMIC_VSPRINTF(str, format);
    -
    205 if (str == NULL)
    -
    206 return false;
    -
    207
    -
    208 bool ret = write_(log, str);
    -
    209
    -
    210 free(str);
    -
    211 return ret;
    -
    212}
    -
    213
    -
    214/**
    -
    215 * qlog->duplicate(): Duplicate log string into other stream
    -
    216 *
    -
    217 * @param log a pointer of qlog_t
    -
    218 * @param fp logging messages will be printed out into this stream.
    -
    219 * set NULL to disable.
    -
    220 * @param flush set to true if you want to flush everytime duplicating.
    -
    221 *
    -
    222 * @return true if successful, otherewise returns false
    -
    223 *
    -
    224 * @code
    -
    225 * log->duplicate(log, stdout, true); // enable console out with flushing
    -
    226 * log->duplicate(log, stderr, false); // enable console out
    -
    227 * log->duplicate(log, NULL, false); // disable console out (default)
    -
    228 * @endcode
    -
    229 */
    -
    230static bool duplicate(qlog_t *log, FILE *outfp, bool flush) {
    -
    231 if (log == NULL)
    -
    232 return false;
    -
    233
    -
    234 Q_MUTEX_ENTER(log->qmutex);
    -
    235 log->outfp = outfp;
    -
    236 log->outflush = flush;
    -
    237 Q_MUTEX_LEAVE(log->qmutex);
    -
    238
    -
    239 return true;
    -
    240}
    -
    241
    -
    242/**
    -
    243 * qlog->flush(): Flush buffered log
    -
    244 *
    -
    245 * @param log a pointer of qlog_t
    -
    246 *
    -
    247 * @return true if successful, otherewise returns false
    -
    248 */
    -
    249static bool flush_(qlog_t *log) {
    -
    250 if (log == NULL)
    -
    251 return false;
    -
    252
    -
    253 // only flush if flush flag is disabled
    -
    254 Q_MUTEX_ENTER(log->qmutex);
    -
    255 if (log->fp != NULL && log->logflush == false)
    -
    256 fflush(log->fp);
    -
    257 if (log->outfp != NULL && log->outflush == false)
    -
    258 fflush(log->outfp);
    -
    259 Q_MUTEX_LEAVE(log->qmutex);
    -
    260
    -
    261 return false;
    -
    262}
    -
    263
    -
    264/**
    -
    265 * qlog->free(): Close ratating-log file & de-allocate resources
    -
    266 *
    -
    267 * @param log a pointer of qlog_t
    -
    268 */
    -
    269static void free_(qlog_t *log) {
    -
    270 if (log == NULL)
    -
    271 return;
    -
    272
    -
    273 flush_(log);
    -
    274 Q_MUTEX_ENTER(log->qmutex);
    -
    275 if (log->fp != NULL) {
    -
    276 fclose(log->fp);
    -
    277 log->fp = NULL;
    -
    278 }
    -
    279 Q_MUTEX_LEAVE(log->qmutex);
    -
    280 Q_MUTEX_DESTROY(log->qmutex);
    -
    281 free(log);
    -
    282 return;
    -
    283}
    -
    284
    -
    285#ifndef _DOXYGEN_SKIP
    -
    286
    -
    287static bool _real_open(qlog_t *log) {
    -
    288 const time_t nowtime = time(NULL);
    -
    289
    -
    290 /* generate filename */
    -
    291 char newfilepath[PATH_MAX];
    -
    292 strftime(newfilepath, sizeof(newfilepath), log->filepathfmt,
    -
    293 localtime(&nowtime));
    -
    294
    -
    295 /* open or re-open log file */
    -
    296 if (log->fp == NULL) {
    -
    297 log->fp = fopen(newfilepath, "a");
    -
    298 if (log->fp == NULL) {
    -
    299 DEBUG("_real_open: Can't open log file '%s'.", newfilepath);
    -
    300 return false;
    -
    301 }
    -
    302
    -
    303 if (log->mode != 0)
    -
    304 fchmod(fileno(log->fp), log->mode);
    -
    305 qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
    -
    306 } else if (strcmp(log->filepath, newfilepath)) {
    -
    307 /* have opened stream, only reopen if new filename is different with
    -
    308 existing one */
    -
    309 FILE *newfp = fopen(newfilepath, "a");
    -
    310 if (newfp != NULL) {
    -
    311 if (log->mode != 0)
    -
    312 fchmod(fileno(newfp), log->mode);
    -
    313 fclose(log->fp);
    -
    314 log->fp = newfp;
    -
    315 qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
    -
    316 } else {
    -
    317 DEBUG("_real_open: Can't open log file '%s' for rotating.",
    -
    318 newfilepath);
    -
    319 }
    -
    320 } else {
    -
    321 DEBUG("_real_open: skip re-opening log file.");
    -
    322 }
    -
    323
    -
    324 /* set next rotate time */
    -
    325 if (log->rotateinterval > 0) {
    -
    326 time_t ct = time(NULL);
    -
    327 time_t dt = ct - mktime(gmtime(&ct));
    -
    328 log->nextrotate = (((ct + dt) / log->rotateinterval) + 1)
    -
    329 * log->rotateinterval - dt;
    -
    330 } else {
    -
    331 log->nextrotate = 0;
    -
    332 }
    -
    333
    -
    334 return true;
    -
    335}
    -
    336
    -
    337#endif
    -
    338
    -
    339#endif /* DISABLE_QLOG */
    -
    static bool writef(qlog_t *log, const char *format,...)
    qlog->writef(): Log messages
    Definition qlog.c:199
    -
    static bool write_(qlog_t *log, const char *str)
    qlog->write(): Log messages
    Definition qlog.c:160
    -
    static bool duplicate(qlog_t *log, FILE *outfp, bool flush)
    qlog->duplicate(): Duplicate log string into other stream
    Definition qlog.c:230
    -
    static void free_(qlog_t *log)
    qlog->free(): Close ratating-log file & de-allocate resources
    Definition qlog.c:269
    -
    static bool flush_(qlog_t *log)
    qlog->flush(): Flush buffered log
    Definition qlog.c:249
    -
    qlog_t * qlog(const char *filepathfmt, mode_t mode, int rotateinterval, int options)
    Open ratating-log file.
    Definition qlog.c:105
    -
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition qstring.c:325
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qlog.c Rotating file logger object.
    +
    31  *
    +
    32  * qlog implements a auto-rotating file logger.
    +
    33  *
    +
    34  * @code
    +
    35  * // create a daily-rotating log file.
    +
    36  * qlog_t *log = qlog("/tmp/dailylog-%Y%m%d.err", 0644, 86400, false);
    +
    37  * if(log == NULL) return;
    +
    38  *
    +
    39  * // screen out.
    +
    40  * log->duplicate(log, stdout, true);
    +
    41  *
    +
    42  * // write logs.
    +
    43  * log->write(log, "Service started.");
    +
    44  * log->writef(log, "Server Id: %d", 1);
    +
    45  *
    +
    46  * // close and release resources.
    +
    47  * log->free(log);
    +
    48  * @endcode
    +
    49  */
    +
    50 
    +
    51 #ifndef DISABLE_QLOG
    +
    52 
    +
    53 #include <stdio.h>
    +
    54 #include <stdlib.h>
    +
    55 #include <stdbool.h>
    +
    56 #include <string.h>
    +
    57 #include <stdarg.h>
    +
    58 #include <unistd.h>
    +
    59 #include <time.h>
    +
    60 #include <sys/stat.h>
    +
    61 #include <errno.h>
    +
    62 #include "qinternal.h"
    +
    63 #include "utilities/qstring.h"
    +
    64 #include "extensions/qlog.h"
    +
    65 
    +
    66 #ifndef _DOXYGEN_SKIP
    +
    67 static bool write_(qlog_t *log, const char *str);
    +
    68 static bool writef(qlog_t *log, const char *format, ...);
    +
    69 static bool duplicate(qlog_t *log, FILE *outfp, bool flush);
    +
    70 static bool flush_(qlog_t *log);
    +
    71 static void free_(qlog_t *log);
    +
    72 
    +
    73 // internal usages
    +
    74 static bool _real_open(qlog_t *log);
    +
    75 #endif
    +
    76 
    +
    77 /**
    +
    78  * Open ratating-log file
    +
    79  *
    +
    80  * @param filepathfmt filename format. formatting argument is same as
    +
    81  * strftime()
    +
    82  * @param mode new file mode. 0 for system default
    +
    83  * @param rotateinterval rotating interval seconds, set 0 to disable rotation
    +
    84  * @param options combination of options.
    +
    85  *
    +
    86  * @return a pointer of qlog_t structure
    +
    87  *
    +
    88  * @note
    +
    89  * rotateinterval is not relative time. If you set it to 3600, log file will be
    +
    90  * rotated at every hour. And filenameformat is same as strftime(). So If you
    +
    91  * want to log with hourly rotating, filenameformat must be defined including
    +
    92  * hour format like "/somepath/xxx-%Y%m%d%H.log". You can set it to
    +
    93  * "/somepath/xxx-%H.log" for daily overrided log file.
    +
    94  *
    +
    95  * @note
    +
    96  * Available options:
    +
    97  * - QLOG_OPT_THREADSAFE - make it thread-safe.
    +
    98  * - QLOG_OPT_FLUSH - flush out buffer everytime.
    +
    99  *
    +
    100  * @code
    +
    101  * qlog_t *log = qlog("/tmp/qdecoder-%Y%m%d.err", 0644, 86400, QLOG_OPT_THREADSAFE);
    +
    102  * log->free(log);
    +
    103  * @endcode
    +
    104  */
    +
    105 qlog_t *qlog(const char *filepathfmt, mode_t mode, int rotateinterval,
    +
    106  int options) {
    +
    107  qlog_t *log;
    +
    108 
    +
    109  // malloc qlog_t structure
    +
    110  log = (qlog_t *) calloc(1, sizeof(qlog_t));
    +
    111  if (log == NULL) {
    +
    112  errno = ENOMEM;
    +
    113  return NULL;
    +
    114  }
    +
    115 
    +
    116  // set up the structure.
    +
    117  qstrcpy(log->filepathfmt, sizeof(log->filepathfmt), filepathfmt);
    +
    118  log->mode = mode;
    +
    119  if (rotateinterval > 0)
    +
    120  log->rotateinterval = rotateinterval;
    +
    121 
    +
    122  // handle options
    +
    123  if (options & QLOG_OPT_THREADSAFE) {
    +
    124  Q_MUTEX_NEW(log->qmutex, true);
    +
    125  if (log->qmutex == NULL) {
    +
    126  errno = ENOMEM;
    +
    127  free(log);
    +
    128  return NULL;
    +
    129  }
    +
    130  }
    +
    131  if (options & QLOG_OPT_FLUSH) {
    +
    132  log->logflush = true;
    +
    133  }
    +
    134 
    +
    135  // try to open the log file.
    +
    136  if (_real_open(log) == false) {
    +
    137  Q_MUTEX_DESTROY(log->qmutex);
    +
    138  free(log);
    +
    139  return NULL;
    +
    140  }
    +
    141 
    +
    142  // member methods
    +
    143  log->write = write_;
    +
    144  log->writef = writef;
    +
    145  log->duplicate = duplicate;
    +
    146  log->flush = flush_;
    +
    147  log->free = free_;
    +
    148 
    +
    149  return log;
    +
    150 }
    +
    151 
    +
    152 /**
    +
    153  * qlog->write(): Log messages
    +
    154  *
    +
    155  * @param log a pointer of qlog_t
    +
    156  * @param str message string
    +
    157  *
    +
    158  * @return true if successful, otherewise returns false
    +
    159  */
    +
    160 static bool write_(qlog_t *log, const char *str) {
    +
    161  if (log == NULL || log->fp == NULL)
    +
    162  return false;
    +
    163 
    +
    164  Q_MUTEX_ENTER(log->qmutex);
    +
    165 
    +
    166  /* duplicate stream */
    +
    167  if (log->outfp != NULL) {
    +
    168  fprintf(log->outfp, "%s\n", str);
    +
    169  if (log->outflush == true)
    +
    170  fflush(log->outfp);
    +
    171  }
    +
    172 
    +
    173  /* check if log rotation is needed */
    +
    174  if (log->nextrotate > 0 && time(NULL) >= log->nextrotate) {
    +
    175  _real_open(log);
    +
    176  }
    +
    177 
    +
    178  /* log to file */
    +
    179  bool ret = false;
    +
    180  if (fprintf(log->fp, "%s\n", str) >= 0) {
    +
    181  if (log->logflush == true)
    +
    182  fflush(log->fp);
    +
    183  ret = true;
    +
    184  }
    +
    185 
    +
    186  Q_MUTEX_LEAVE(log->qmutex);
    +
    187 
    +
    188  return ret;
    +
    189 }
    +
    190 
    +
    191 /**
    +
    192  * qlog->writef(): Log messages
    +
    193  *
    +
    194  * @param log a pointer of qlog_t
    +
    195  * @param format messages format
    +
    196  *
    +
    197  * @return true if successful, otherewise returns false
    +
    198  */
    +
    199 static bool writef(qlog_t *log, const char *format, ...) {
    +
    200  if (log == NULL || log->fp == NULL)
    +
    201  return false;
    +
    202 
    +
    203  char *str;
    +
    204  DYNAMIC_VSPRINTF(str, format);
    +
    205  if (str == NULL)
    +
    206  return false;
    +
    207 
    +
    208  bool ret = write_(log, str);
    +
    209 
    +
    210  free(str);
    +
    211  return ret;
    +
    212 }
    +
    213 
    +
    214 /**
    +
    215  * qlog->duplicate(): Duplicate log string into other stream
    +
    216  *
    +
    217  * @param log a pointer of qlog_t
    +
    218  * @param fp logging messages will be printed out into this stream.
    +
    219  * set NULL to disable.
    +
    220  * @param flush set to true if you want to flush everytime duplicating.
    +
    221  *
    +
    222  * @return true if successful, otherewise returns false
    +
    223  *
    +
    224  * @code
    +
    225  * log->duplicate(log, stdout, true); // enable console out with flushing
    +
    226  * log->duplicate(log, stderr, false); // enable console out
    +
    227  * log->duplicate(log, NULL, false); // disable console out (default)
    +
    228  * @endcode
    +
    229  */
    +
    230 static bool duplicate(qlog_t *log, FILE *outfp, bool flush) {
    +
    231  if (log == NULL)
    +
    232  return false;
    +
    233 
    +
    234  Q_MUTEX_ENTER(log->qmutex);
    +
    235  log->outfp = outfp;
    +
    236  log->outflush = flush;
    +
    237  Q_MUTEX_LEAVE(log->qmutex);
    +
    238 
    +
    239  return true;
    +
    240 }
    +
    241 
    +
    242 /**
    +
    243  * qlog->flush(): Flush buffered log
    +
    244  *
    +
    245  * @param log a pointer of qlog_t
    +
    246  *
    +
    247  * @return true if successful, otherewise returns false
    +
    248  */
    +
    249 static bool flush_(qlog_t *log) {
    +
    250  if (log == NULL)
    +
    251  return false;
    +
    252 
    +
    253  // only flush if flush flag is disabled
    +
    254  Q_MUTEX_ENTER(log->qmutex);
    +
    255  if (log->fp != NULL && log->logflush == false)
    +
    256  fflush(log->fp);
    +
    257  if (log->outfp != NULL && log->outflush == false)
    +
    258  fflush(log->outfp);
    +
    259  Q_MUTEX_LEAVE(log->qmutex);
    +
    260 
    +
    261  return false;
    +
    262 }
    +
    263 
    +
    264 /**
    +
    265  * qlog->free(): Close ratating-log file & de-allocate resources
    +
    266  *
    +
    267  * @param log a pointer of qlog_t
    +
    268  */
    +
    269 static void free_(qlog_t *log) {
    +
    270  if (log == NULL)
    +
    271  return;
    +
    272 
    +
    273  flush_(log);
    +
    274  Q_MUTEX_ENTER(log->qmutex);
    +
    275  if (log->fp != NULL) {
    +
    276  fclose(log->fp);
    +
    277  log->fp = NULL;
    +
    278  }
    +
    279  Q_MUTEX_LEAVE(log->qmutex);
    +
    280  Q_MUTEX_DESTROY(log->qmutex);
    +
    281  free(log);
    +
    282  return;
    +
    283 }
    +
    284 
    +
    285 #ifndef _DOXYGEN_SKIP
    +
    286 
    +
    287 static bool _real_open(qlog_t *log) {
    +
    288  const time_t nowtime = time(NULL);
    +
    289 
    +
    290  /* generate filename */
    +
    291  char newfilepath[PATH_MAX];
    +
    292  strftime(newfilepath, sizeof(newfilepath), log->filepathfmt,
    +
    293  localtime(&nowtime));
    +
    294 
    +
    295  /* open or re-open log file */
    +
    296  if (log->fp == NULL) {
    +
    297  log->fp = fopen(newfilepath, "a");
    +
    298  if (log->fp == NULL) {
    +
    299  DEBUG("_real_open: Can't open log file '%s'.", newfilepath);
    +
    300  return false;
    +
    301  }
    +
    302 
    +
    303  if (log->mode != 0)
    +
    304  fchmod(fileno(log->fp), log->mode);
    +
    305  qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
    +
    306  } else if (strcmp(log->filepath, newfilepath)) {
    +
    307  /* have opened stream, only reopen if new filename is different with
    +
    308  existing one */
    +
    309  FILE *newfp = fopen(newfilepath, "a");
    +
    310  if (newfp != NULL) {
    +
    311  if (log->mode != 0)
    +
    312  fchmod(fileno(newfp), log->mode);
    +
    313  fclose(log->fp);
    +
    314  log->fp = newfp;
    +
    315  qstrcpy(log->filepath, sizeof(log->filepath), newfilepath);
    +
    316  } else {
    +
    317  DEBUG("_real_open: Can't open log file '%s' for rotating.",
    +
    318  newfilepath);
    +
    319  }
    +
    320  } else {
    +
    321  DEBUG("_real_open: skip re-opening log file.");
    +
    322  }
    +
    323 
    +
    324  /* set next rotate time */
    +
    325  if (log->rotateinterval > 0) {
    +
    326  time_t ct = time(NULL);
    +
    327  time_t dt = ct - mktime(gmtime(&ct));
    +
    328  log->nextrotate = (((ct + dt) / log->rotateinterval) + 1)
    +
    329  * log->rotateinterval - dt;
    +
    330  } else {
    +
    331  log->nextrotate = 0;
    +
    332  }
    +
    333 
    +
    334  return true;
    +
    335 }
    +
    336 
    +
    337 #endif
    +
    338 
    +
    339 #endif /* DISABLE_QLOG */
    +
    static bool writef(qlog_t *log, const char *format,...)
    qlog->writef(): Log messages
    Definition: qlog.c:199
    +
    static bool write_(qlog_t *log, const char *str)
    qlog->write(): Log messages
    Definition: qlog.c:160
    +
    static bool duplicate(qlog_t *log, FILE *outfp, bool flush)
    qlog->duplicate(): Duplicate log string into other stream
    Definition: qlog.c:230
    +
    static void free_(qlog_t *log)
    qlog->free(): Close ratating-log file & de-allocate resources
    Definition: qlog.c:269
    +
    static bool flush_(qlog_t *log)
    qlog->flush(): Flush buffered log
    Definition: qlog.c:249
    +
    qlog_t * qlog(const char *filepathfmt, mode_t mode, int rotateinterval, int options)
    Open ratating-log file.
    Definition: qlog.c:105
    +
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition: qstring.c:325
    diff --git a/doc/html/qqueue_8c.html b/doc/html/qqueue_8c.html index 8186f08a..17553da9 100644 --- a/doc/html/qqueue_8c.html +++ b/doc/html/qqueue_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qqueue.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@

    @@ -61,7 +60,8 @@
    -
    qqueue.c File Reference
    +
    +
    qqueue.c File Reference
    @@ -70,58 +70,58 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - - - - - - + + + + + + - + - - - - - - - - - + + + + + + + + + - + - - - + + + - + - + - + - +

    +

    Functions

    qqueue_t * qqueue (int options)
     Create new queue container.
     
    qqueue_t * qqueue (int options)
     Create new queue container. More...
     
    size_t qqueue_setsize (qqueue_t *queue, size_t max)
     qqueue->setsize(): Sets maximum number of elements allowed in this queue.
     qqueue->setsize(): Sets maximum number of elements allowed in this queue. More...
     
    bool qqueue_push (qqueue_t *queue, const void *data, size_t size)
     qqueue->push(): Pushes an element onto the top of this queue.
     qqueue->push(): Pushes an element onto the top of this queue. More...
     
    bool qqueue_pushstr (qqueue_t *queue, const char *str)
     qqueue->pushstr(): Pushes a string onto the top of this queue.
     qqueue->pushstr(): Pushes a string onto the top of this queue. More...
     
    bool qqueue_pushint (qqueue_t *queue, int64_t num)
     qqueue->pushint(): Pushes a integer onto the top of this queue.
     qqueue->pushint(): Pushes a integer onto the top of this queue. More...
     
    void * qqueue_pop (qqueue_t *queue, size_t *size)
     qqueue->pop(): Removes a element at the top of this queue and returns that element.
     
    char * qqueue_popstr (qqueue_t *queue)
     qqueue->popstr(): Removes a element at the top of this queue and returns that element.
     
    void * qqueue_pop (qqueue_t *queue, size_t *size)
     qqueue->pop(): Removes a element at the top of this queue and returns that element. More...
     
    char * qqueue_popstr (qqueue_t *queue)
     qqueue->popstr(): Removes a element at the top of this queue and returns that element. More...
     
    int64_t qqueue_popint (qqueue_t *queue)
     qqueue->popint(): Removes a integer at the top of this queue and returns that element.
     qqueue->popint(): Removes a integer at the top of this queue and returns that element. More...
     
    void * qqueue_popat (qqueue_t *queue, int index, size_t *size)
     qqueue->popat(): Returns and remove the element at the specified position in this queue.
     
    void * qqueue_get (qqueue_t *queue, size_t *size, bool newmem)
     qqueue->get(): Returns an element at the top of this queue without removing it.
     
    char * qqueue_getstr (qqueue_t *queue)
     qqueue->getstr(): Returns an string at the top of this queue without removing it.
     
    void * qqueue_popat (qqueue_t *queue, int index, size_t *size)
     qqueue->popat(): Returns and remove the element at the specified position in this queue. More...
     
    void * qqueue_get (qqueue_t *queue, size_t *size, bool newmem)
     qqueue->get(): Returns an element at the top of this queue without removing it. More...
     
    char * qqueue_getstr (qqueue_t *queue)
     qqueue->getstr(): Returns an string at the top of this queue without removing it. More...
     
    int64_t qqueue_getint (qqueue_t *queue)
     qqueue->getint(): Returns an integer at the top of this queue without removing it.
     qqueue->getint(): Returns an integer at the top of this queue without removing it. More...
     
    void * qqueue_getat (qqueue_t *queue, int index, size_t *size, bool newmem)
     qqueue->getat(): Returns an element at the specified position in this queue without removing it.
     
    void * qqueue_getat (qqueue_t *queue, int index, size_t *size, bool newmem)
     qqueue->getat(): Returns an element at the specified position in this queue without removing it. More...
     
    size_t qqueue_size (qqueue_t *queue)
     qqueue->size(): Returns the number of elements in this queue.
     qqueue->size(): Returns the number of elements in this queue. More...
     
    void qqueue_clear (qqueue_t *queue)
     qqueue->clear(): Removes all of the elements from this queue.
     qqueue->clear(): Removes all of the elements from this queue. More...
     
    bool qqueue_debug (qqueue_t *queue, FILE *out)
     qqueue->debug(): Print out stored elements for debugging purpose.
     qqueue->debug(): Print out stored elements for debugging purpose. More...
     
    void qqueue_free (qqueue_t *queue)
     qqueue->free(): Free qqueue_t
     qqueue->free(): Free qqueue_t More...
     

    Detailed Description

    @@ -191,14 +191,14 @@

    Definition in file qqueue.c.

    Function Documentation

    - -

    ◆ qqueue()

    + +

    ◆ qqueue()

    - + @@ -234,8 +234,8 @@

    -

    ◆ qqueue_setsize()

    + +

    ◆ qqueue_setsize()

    @@ -274,8 +274,8 @@

    -

    ◆ qqueue_push()

    + +

    ◆ qqueue_push()

    @@ -332,8 +332,8 @@

    -

    ◆ qqueue_pushstr()

    + +

    ◆ qqueue_pushstr()

    @@ -384,8 +384,8 @@

    -

    ◆ qqueue_pushint()

    + +

    ◆ qqueue_pushint()

    @@ -434,14 +434,14 @@

    -

    ◆ qqueue_pop()

    + +

    ◆ qqueue_pop()

    qqueue_t * qqueue qqueue_t* qqueue ( int  options)
    - + @@ -484,14 +484,14 @@

    -

    ◆ qqueue_popstr()

    + +

    ◆ qqueue_popstr()

    void * qqueue_pop void* qqueue_pop ( qqueue_t *  queue,
    - + @@ -524,8 +524,8 @@

    -

    ◆ qqueue_popint()

    + +

    ◆ qqueue_popint()

    @@ -564,14 +564,14 @@

    -

    ◆ qqueue_popat()

    + +

    ◆ qqueue_popat()

    char * qqueue_popstr char* qqueue_popstr ( qqueue_t *  queue)
    - + @@ -622,14 +622,14 @@

    -

    ◆ qqueue_get()

    + +

    ◆ qqueue_get()

    void * qqueue_popat void* qqueue_popat ( qqueue_t *  queue,
    - + @@ -679,14 +679,14 @@

    -

    ◆ qqueue_getstr()

    + +

    ◆ qqueue_getstr()

    void * qqueue_get void* qqueue_get ( qqueue_t *  queue,
    - + @@ -719,8 +719,8 @@

    -

    ◆ qqueue_getint()

    + +

    ◆ qqueue_getint()

    @@ -759,14 +759,14 @@

    -

    ◆ qqueue_getat()

    + +

    ◆ qqueue_getat()

    char * qqueue_getstr char* qqueue_getstr ( qqueue_t *  queue)
    - + @@ -824,8 +824,8 @@

    -

    ◆ qqueue_size()

    + +

    ◆ qqueue_size()

    @@ -853,8 +853,8 @@

    -

    ◆ qqueue_clear()

    + +

    ◆ qqueue_clear()

    @@ -881,8 +881,8 @@

    -

    ◆ qqueue_debug()

    + +

    ◆ qqueue_debug()

    @@ -921,8 +921,8 @@

    -

    ◆ qqueue_free()

    + +

    ◆ qqueue_free()

    @@ -956,7 +956,7 @@

    diff --git a/doc/html/qqueue_8c.js b/doc/html/qqueue_8c.js index 0aecf55f..09a9294c 100644 --- a/doc/html/qqueue_8c.js +++ b/doc/html/qqueue_8c.js @@ -1,18 +1,18 @@ var qqueue_8c = [ - [ "qqueue", "qqueue_8c.html#abd187f46c2a60edaa0b1bd82bb8a1426", null ], + [ "qqueue", "qqueue_8c.html#a7bd8537313542cb0db0c912fce4af056", null ], [ "qqueue_setsize", "qqueue_8c.html#ada41104b85c01a5773263c3ff84f9dc5", null ], [ "qqueue_push", "qqueue_8c.html#a3111eeb954b1af9c0c8a67f96968b988", null ], [ "qqueue_pushstr", "qqueue_8c.html#a382fb2a1977afc2b129bded75a41ed8b", null ], [ "qqueue_pushint", "qqueue_8c.html#abe2952d24a9a9f6cc8121f491faec4c4", null ], - [ "qqueue_pop", "qqueue_8c.html#a6859700f743f55461f8ca56beba6437f", null ], - [ "qqueue_popstr", "qqueue_8c.html#a7250d2c5928e63093344e559f629fbbc", null ], + [ "qqueue_pop", "qqueue_8c.html#a86b2d6fab3d2426cda120d46e17ec65a", null ], + [ "qqueue_popstr", "qqueue_8c.html#a95ad438031ccd21b02169b9b685851c3", null ], [ "qqueue_popint", "qqueue_8c.html#ade3de0f1655569417856bb9758f1017c", null ], - [ "qqueue_popat", "qqueue_8c.html#aa72722b6d5a7ecc5ec01f71c0d7aa951", null ], - [ "qqueue_get", "qqueue_8c.html#a0a561f175ff24b26dea93b3540363e4d", null ], - [ "qqueue_getstr", "qqueue_8c.html#a984579020101ea2eec6f6e074148923b", null ], + [ "qqueue_popat", "qqueue_8c.html#a4e9a5e7b48086a5335de5047ab7da8f3", null ], + [ "qqueue_get", "qqueue_8c.html#a08067efa9b39e73c2984f58a3d045906", null ], + [ "qqueue_getstr", "qqueue_8c.html#ac04a0dcde8dba0b43c1c8ee3aa87035e", null ], [ "qqueue_getint", "qqueue_8c.html#a00afe6738073f11568f5b75c11bd11a2", null ], - [ "qqueue_getat", "qqueue_8c.html#ae22aa53bd411431a0809888655797848", null ], + [ "qqueue_getat", "qqueue_8c.html#a947c732d507e74898625d385c7595d00", null ], [ "qqueue_size", "qqueue_8c.html#a732d5260a04c2f67fa28f690eb31566c", null ], [ "qqueue_clear", "qqueue_8c.html#a407c506258030896676079f9125132b9", null ], [ "qqueue_debug", "qqueue_8c.html#a39066762f093f61a310dc3b3550c8780", null ], diff --git a/doc/html/qqueue_8c_source.html b/doc/html/qqueue_8c_source.html index 8ccace22..abd60f8d 100644 --- a/doc/html/qqueue_8c_source.html +++ b/doc/html/qqueue_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qqueue.c Source File @@ -20,8 +20,8 @@

    void * qqueue_getat void* qqueue_getat ( qqueue_t *  queue,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,495 +52,496 @@

    -
    qqueue.c
    +
    +
    qqueue.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qqueue.c Queue implementation.
    -
    31 *
    -
    32 * qqueue container is a queue implementation. It represents a
    -
    33 * first-in-first-out(FIFO). It extends qlist container that allow a
    -
    34 * linked-list to be treated as a queue.
    -
    35 *
    -
    36 * @code
    -
    37 * [Conceptional Data Structure Diagram]
    -
    38 *
    -
    39 * top bottom
    -
    40 * DATA POP <== [ A ][ B ][ C ] <== DATA PUSH
    -
    41 * (positive index) 0 1 2
    -
    42 * (negative index) -3 -2 -1
    -
    43 * @endcode
    -
    44 *
    -
    45 * @code
    -
    46 * // create queue
    -
    47 * qqueue_t *queue = qQueue();
    -
    48 *
    -
    49 * // example: integer queue
    -
    50 * queue->pushint(queue, 1);
    -
    51 * queue->pushint(queue, 2);
    -
    52 * queue->pushint(queue, 3);
    -
    53 *
    -
    54 * printf("popint(): %d\n", queue->popint(queue));
    -
    55 * printf("popint(): %d\n", queue->popint(queue));
    -
    56 * printf("popint(): %d\n", queue->popint(queue));
    -
    57 *
    -
    58 * // example: string queue
    -
    59 * queue->pushstr(queue, "A string");
    -
    60 * queue->pushstr(queue, "B string");
    -
    61 * queue->pushstr(queue, "C string");
    -
    62 *
    -
    63 * char *str = queue->popstr(queue);
    -
    64 * printf("popstr(): %s\n", str);
    -
    65 * free(str);
    -
    66 * str = queue->popstr(queue);
    -
    67 * printf("popstr(): %s\n", str);
    -
    68 * free(str);
    -
    69 * str = queue->popstr(queue);
    -
    70 * printf("popstr(): %s\n", str);
    -
    71 * free(str);
    -
    72 *
    -
    73 * // example: object queue
    -
    74 * queue->push(queue, "A object", sizeof("A object"));
    -
    75 * queue->push(queue, "B object", sizeof("B object"));
    -
    76 * queue->push(queue, "C object", sizeof("C object"));
    -
    77 *
    -
    78 * void *obj = queue->pop(queue, NULL);
    -
    79 * printf("pop(): %s\n", (char*)obj);
    -
    80 * free(obj);
    -
    81 * obj = queue->pop(queue, NULL);
    -
    82 * printf("pop(): %s\n", (char*)obj);
    -
    83 * free(obj);
    -
    84 * obj = queue->pop(queue, NULL);
    -
    85 * printf("pop(): %s\n", (char*)obj);
    -
    86 * free(obj);
    -
    87 *
    -
    88 * // release
    -
    89 * queue->free(queue);
    -
    90 *
    -
    91 * [Output]
    -
    92 * popint(): 3
    -
    93 * popint(): 2
    -
    94 * popint(): 1
    -
    95 * popstr(): C string
    -
    96 * popstr(): B string
    -
    97 * popstr(): A string
    -
    98 * pop(): C object
    -
    99 * pop(): B object
    -
    100 * pop(): A object
    -
    101 * @endcode
    -
    102 */
    -
    103
    -
    104#include <stdio.h>
    -
    105#include <stdlib.h>
    -
    106#include <stdbool.h>
    -
    107#include <string.h>
    -
    108#include <errno.h>
    -
    109#include "qinternal.h"
    -
    110#include "containers/qqueue.h"
    -
    111
    -
    112/**
    -
    113 * Create new queue container
    -
    114 *
    -
    115 * @param options combination of initialization options.
    -
    116 *
    -
    117 * @return a pointer of malloced qqueue container, otherwise returns NULL.
    -
    118 * @retval errno will be set in error condition.
    -
    119 * - ENOMEM : Memory allocation failure.
    -
    120 *
    -
    121 * @code
    -
    122 * qqueue_t *queue = qQueue(QQUEUE_THREADSAFE);
    -
    123 * @endcode
    -
    124 *
    -
    125 * @note
    -
    126 * Available options:
    -
    127 * - QQUEUE_THREADSAFE - make it thread-safe.
    -
    128 */
    -
    129qqueue_t *qqueue(int options) {
    -
    130 qqueue_t *queue = (qqueue_t *) malloc(sizeof(qqueue_t));
    -
    131 if (queue == NULL) {
    -
    132 errno = ENOMEM;
    -
    133 return NULL;
    -
    134 }
    -
    135
    -
    136 memset((void *) queue, 0, sizeof(qqueue_t));
    -
    137 queue->list = qlist(options);
    -
    138 if (queue->list == NULL) {
    -
    139 free(queue);
    -
    140 return NULL;
    -
    141 }
    -
    142
    -
    143 // methods
    -
    144 queue->setsize = qqueue_setsize;
    -
    145
    -
    146 queue->push = qqueue_push;
    -
    147 queue->pushstr = qqueue_pushstr;
    -
    148 queue->pushint = qqueue_pushint;
    -
    149
    -
    150 queue->pop = qqueue_pop;
    -
    151 queue->popstr = qqueue_popstr;
    -
    152 queue->popint = qqueue_popint;
    -
    153 queue->popat = qqueue_popat;
    -
    154
    -
    155 queue->get = qqueue_get;
    -
    156 queue->getstr = qqueue_getstr;
    -
    157 queue->getint = qqueue_getint;
    -
    158 queue->getat = qqueue_getat;
    -
    159
    -
    160 queue->size = qqueue_size;
    -
    161 queue->clear = qqueue_clear;
    -
    162 queue->debug = qqueue_debug;
    -
    163 queue->free = qqueue_free;
    -
    164
    -
    165 return queue;
    -
    166}
    -
    167
    -
    168/**
    -
    169 * qqueue->setsize(): Sets maximum number of elements allowed in this
    -
    170 * queue.
    -
    171 *
    -
    172 * @param queue qqueue container pointer.
    -
    173 * @param max maximum number of elements. 0 means no limit.
    -
    174 *
    -
    175 * @return previous maximum number.
    -
    176 */
    -
    177size_t qqueue_setsize(qqueue_t *queue, size_t max) {
    -
    178 return queue->list->setsize(queue->list, max);
    -
    179}
    -
    180
    -
    181/**
    -
    182 * qqueue->push(): Pushes an element onto the top of this queue.
    -
    183 *
    -
    184 * @param queue qqueue container pointer.
    -
    185 * @param data a pointer which points data memory.
    -
    186 * @param size size of the data.
    -
    187 *
    -
    188 * @return true if successful, otherwise returns false.
    -
    189 * @retval errno will be set in error condition.
    -
    190 * - EINVAL : Invalid argument.
    -
    191 * - ENOBUFS : Queue full. Only happens when this queue has set to have
    -
    192 * limited number of elements)
    -
    193 * - ENOMEM : Memory allocation failure.
    -
    194 */
    -
    195bool qqueue_push(qqueue_t *queue, const void *data, size_t size) {
    -
    196 return queue->list->addlast(queue->list, data, size);
    -
    197}
    -
    198
    -
    199/**
    -
    200 * qqueue->pushstr(): Pushes a string onto the top of this queue.
    -
    201 *
    -
    202 * @param queue qqueue container pointer.
    -
    203 * @param data a pointer which points data memory.
    -
    204 * @param size size of the data.
    -
    205 *
    -
    206 * @return true if successful, otherwise returns false.
    -
    207 * @retval errno will be set in error condition.
    -
    208 * - EINVAL : Invalid argument.
    -
    209 * - ENOBUFS : Queue full. Only happens when this queue has set to have
    -
    210 * limited number of elements.
    -
    211 * - ENOMEM : Memory allocation failure.
    -
    212 */
    -
    213bool qqueue_pushstr(qqueue_t *queue, const char *str) {
    -
    214 if (str == NULL) {
    -
    215 errno = EINVAL;
    -
    216 return false;
    -
    217 }
    -
    218 return queue->list->addlast(queue->list, str, strlen(str) + 1);
    -
    219}
    -
    220
    -
    221/**
    -
    222 * qqueue->pushint(): Pushes a integer onto the top of this queue.
    -
    223 *
    -
    224 * @param queue qqueue container pointer.
    -
    225 * @param num integer data.
    -
    226 *
    -
    227 * @return true if successful, otherwise returns false.
    -
    228 * @retval errno will be set in error condition.
    -
    229 * - ENOBUFS : Queue full. Only happens when this queue has set to have
    -
    230 * limited number of elements.
    -
    231 * - ENOMEM : Memory allocation failure.
    -
    232 */
    -
    233bool qqueue_pushint(qqueue_t *queue, int64_t num) {
    -
    234 return queue->list->addlast(queue->list, &num, sizeof(num));
    -
    235}
    -
    236
    -
    237/**
    -
    238 * qqueue->pop(): Removes a element at the top of this queue and returns
    -
    239 * that element.
    -
    240 *
    -
    241 * @param queue qqueue container pointer.
    -
    242 * @param size if size is not NULL, element size will be stored.
    -
    243 *
    -
    244 * @return a pointer of malloced element, otherwise returns NULL.
    -
    245 * @retval errno will be set in error condition.
    -
    246 * - ENOENT : Queue is empty.
    -
    247 * - ENOMEM : Memory allocation failure.
    -
    248 */
    -
    249void *qqueue_pop(qqueue_t *queue, size_t *size) {
    -
    250 return queue->list->popfirst(queue->list, size);
    -
    251}
    -
    252
    -
    253/**
    -
    254 * qqueue->popstr(): Removes a element at the top of this queue and
    -
    255 * returns that element.
    -
    256 *
    -
    257 * @param queue qqueue container pointer.
    -
    258 *
    -
    259 * @return a pointer of malloced string element, otherwise returns NULL.
    -
    260 * @retval errno will be set in error condition.
    -
    261 * - ENOENT : Queue is empty.
    -
    262 * - ENOMEM : Memory allocation failure.
    -
    263 *
    -
    264 * @note
    -
    265 * The string element should be pushed through pushstr().
    -
    266 */
    -
    267char *qqueue_popstr(qqueue_t *queue) {
    -
    268 size_t strsize;
    -
    269 char *str = queue->list->popfirst(queue->list, &strsize);
    -
    270 if (str != NULL) {
    -
    271 str[strsize - 1] = '\0'; // just to make sure
    -
    272 }
    -
    273
    -
    274 return str;
    -
    275}
    -
    276
    -
    277/**
    -
    278 * qqueue->popint(): Removes a integer at the top of this queue and
    -
    279 * returns that element.
    -
    280 *
    -
    281 * @param queue qqueue container pointer.
    -
    282 *
    -
    283 * @return an integer value, otherwise returns 0.
    -
    284 * @retval errno will be set in error condition.
    -
    285 * - ENOENT : Queue is empty.
    -
    286 * - ENOMEM : Memory allocation failure.
    -
    287 *
    -
    288 * @note
    -
    289 * The integer element should be pushed through pushint().
    -
    290 */
    -
    291int64_t qqueue_popint(qqueue_t *queue) {
    -
    292 int64_t num = 0;
    -
    293 int64_t *pnum = queue->list->popfirst(queue->list, NULL);
    -
    294 if (pnum != NULL) {
    -
    295 num = *pnum;
    -
    296 free(pnum);
    -
    297 }
    -
    298
    -
    299 return num;
    -
    300}
    -
    301
    -
    302/**
    -
    303 * qqueue->popat(): Returns and remove the element at the specified
    -
    304 * position in this queue.
    -
    305 *
    -
    306 * @param queue qqueue container pointer.
    -
    307 * @param index index at which the specified element is to be inserted
    -
    308 * @param size if size is not NULL, element size will be stored.
    -
    309 *
    -
    310 * @return a pointer of malloced element, otherwise returns NULL.
    -
    311 * @retval errno will be set in error condition.
    -
    312 * - ERANGE : Index out of range.
    -
    313 * - ENOMEM : Memory allocation failure.
    -
    314 *
    -
    315 * @note
    -
    316 * Negative index can be used for addressing a element from the bottom in this
    -
    317 * queue. For example, index -1 will always pop a element which is pushed at
    -
    318 * very last time.
    -
    319 */
    -
    320void *qqueue_popat(qqueue_t *queue, int index, size_t *size) {
    -
    321 return queue->list->popat(queue->list, index, size);
    -
    322}
    -
    323
    -
    324/**
    -
    325 * qqueue->get(): Returns an element at the top of this queue without
    -
    326 * removing it.
    -
    327 *
    -
    328 * @param queue qqueue container pointer.
    -
    329 * @param size if size is not NULL, element size will be stored.
    -
    330 * @param newmem whether or not to allocate memory for the element.
    -
    331 *
    -
    332 * @return a pointer of malloced element, otherwise returns NULL.
    -
    333 * @retval errno will be set in error condition.
    -
    334 * - ENOENT : Queue is empty.
    -
    335 * - ENOMEM : Memory allocation failure.
    -
    336 */
    -
    337void *qqueue_get(qqueue_t *queue, size_t *size, bool newmem) {
    -
    338 return queue->list->getfirst(queue->list, size, newmem);
    -
    339}
    -
    340
    -
    341/**
    -
    342 * qqueue->getstr(): Returns an string at the top of this queue without
    -
    343 * removing it.
    -
    344 *
    -
    345 * @param queue qqueue container pointer.
    -
    346 *
    -
    347 * @return a pointer of malloced string element, otherwise returns NULL.
    -
    348 * @retval errno will be set in error condition.
    -
    349 * - ENOENT : Queue is empty.
    -
    350 * - ENOMEM : Memory allocation failure.
    -
    351 *
    -
    352 * @note
    -
    353 * The string element should be pushed through pushstr().
    -
    354 */
    -
    355char *qqueue_getstr(qqueue_t *queue) {
    -
    356 size_t strsize;
    -
    357 char *str = queue->list->getfirst(queue->list, &strsize, true);
    -
    358 if (str != NULL) {
    -
    359 str[strsize - 1] = '\0'; // just to make sure
    -
    360 }
    -
    361
    -
    362 return str;
    -
    363}
    -
    364
    -
    365/**
    -
    366 * qqueue->getint(): Returns an integer at the top of this queue without
    -
    367 * removing it.
    -
    368 *
    -
    369 * @param queue qqueue container pointer.
    -
    370 *
    -
    371 * @return an integer value, otherwise returns 0.
    -
    372 * @retval errno will be set in error condition.
    -
    373 * - ENOENT : Queue is empty.
    -
    374 * - ENOMEM : Memory allocation failure.
    -
    375 *
    -
    376 * @note
    -
    377 * The integer element should be pushed through pushint().
    -
    378 */
    -
    379int64_t qqueue_getint(qqueue_t *queue) {
    -
    380 int64_t num = 0;
    -
    381 int64_t *pnum = queue->list->getfirst(queue->list, NULL, true);
    -
    382 if (pnum != NULL) {
    -
    383 num = *pnum;
    -
    384 free(pnum);
    -
    385 }
    -
    386
    -
    387 return num;
    -
    388}
    -
    389
    -
    390/**
    -
    391 * qqueue->getat(): Returns an element at the specified position in this
    -
    392 * queue without removing it.
    -
    393 *
    -
    394 * @param queue qqueue container pointer.
    -
    395 * @param index index at which the specified element is to be inserted
    -
    396 * @param size if size is not NULL, element size will be stored.
    -
    397 * @param newmem whether or not to allocate memory for the element.
    -
    398 *
    -
    399 * @return a pointer of element, otherwise returns NULL.
    -
    400 * @retval errno will be set in error condition.
    -
    401 * - ERANGE : Index out of range.
    -
    402 * - ENOMEM : Memory allocation failure.
    -
    403 *
    -
    404 * @note
    -
    405 * Negative index can be used for addressing a element from the bottom in this
    -
    406 * queue. For example, index -1 will always get a element which is pushed at
    -
    407 * very last time.
    -
    408 */
    -
    409void *qqueue_getat(qqueue_t *queue, int index, size_t *size, bool newmem) {
    -
    410 return queue->list->getat(queue->list, index, size, newmem);
    -
    411}
    -
    412
    -
    413/**
    -
    414 * qqueue->size(): Returns the number of elements in this queue.
    -
    415 *
    -
    416 * @param queue qqueue container pointer.
    -
    417 *
    -
    418 * @return the number of elements in this queue.
    -
    419 */
    -
    420size_t qqueue_size(qqueue_t *queue) {
    -
    421 return queue->list->size(queue->list);
    -
    422}
    -
    423
    -
    424/**
    -
    425 * qqueue->clear(): Removes all of the elements from this queue.
    -
    426 *
    -
    427 * @param queue qqueue container pointer.
    -
    428 */
    -
    429void qqueue_clear(qqueue_t *queue) {
    -
    430 queue->list->clear(queue->list);
    -
    431}
    -
    432
    -
    433/**
    -
    434 * qqueue->debug(): Print out stored elements for debugging purpose.
    -
    435 *
    -
    436 * @param queue qqueue container pointer.
    -
    437 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    438 *
    -
    439 * @return true if successful, otherwise returns false.
    -
    440 */
    -
    441bool qqueue_debug(qqueue_t *queue, FILE *out) {
    -
    442 return queue->list->debug(queue->list, out);
    -
    443}
    -
    444
    -
    445/**
    -
    446 * qqueue->free(): Free qqueue_t
    -
    447 *
    -
    448 * @param queue qqueue container pointer.
    -
    449 *
    -
    450 * @return always returns true.
    -
    451 */
    -
    452void qqueue_free(qqueue_t *queue) {
    -
    453 queue->list->free(queue->list);
    -
    454 free(queue);
    -
    455}
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    -
    int64_t qqueue_getint(qqueue_t *queue)
    qqueue->getint(): Returns an integer at the top of this queue without removing it.
    Definition qqueue.c:379
    -
    void * qqueue_get(qqueue_t *queue, size_t *size, bool newmem)
    qqueue->get(): Returns an element at the top of this queue without removing it.
    Definition qqueue.c:337
    -
    bool qqueue_push(qqueue_t *queue, const void *data, size_t size)
    qqueue->push(): Pushes an element onto the top of this queue.
    Definition qqueue.c:195
    -
    bool qqueue_pushstr(qqueue_t *queue, const char *str)
    qqueue->pushstr(): Pushes a string onto the top of this queue.
    Definition qqueue.c:213
    -
    bool qqueue_debug(qqueue_t *queue, FILE *out)
    qqueue->debug(): Print out stored elements for debugging purpose.
    Definition qqueue.c:441
    -
    void qqueue_clear(qqueue_t *queue)
    qqueue->clear(): Removes all of the elements from this queue.
    Definition qqueue.c:429
    -
    void * qqueue_pop(qqueue_t *queue, size_t *size)
    qqueue->pop(): Removes a element at the top of this queue and returns that element.
    Definition qqueue.c:249
    -
    char * qqueue_popstr(qqueue_t *queue)
    qqueue->popstr(): Removes a element at the top of this queue and returns that element.
    Definition qqueue.c:267
    -
    size_t qqueue_size(qqueue_t *queue)
    qqueue->size(): Returns the number of elements in this queue.
    Definition qqueue.c:420
    -
    void qqueue_free(qqueue_t *queue)
    qqueue->free(): Free qqueue_t
    Definition qqueue.c:452
    -
    char * qqueue_getstr(qqueue_t *queue)
    qqueue->getstr(): Returns an string at the top of this queue without removing it.
    Definition qqueue.c:355
    -
    void * qqueue_popat(qqueue_t *queue, int index, size_t *size)
    qqueue->popat(): Returns and remove the element at the specified position in this queue.
    Definition qqueue.c:320
    -
    qqueue_t * qqueue(int options)
    Create new queue container.
    Definition qqueue.c:129
    -
    bool qqueue_pushint(qqueue_t *queue, int64_t num)
    qqueue->pushint(): Pushes a integer onto the top of this queue.
    Definition qqueue.c:233
    -
    size_t qqueue_setsize(qqueue_t *queue, size_t max)
    qqueue->setsize(): Sets maximum number of elements allowed in this queue.
    Definition qqueue.c:177
    -
    int64_t qqueue_popint(qqueue_t *queue)
    qqueue->popint(): Removes a integer at the top of this queue and returns that element.
    Definition qqueue.c:291
    -
    void * qqueue_getat(qqueue_t *queue, int index, size_t *size, bool newmem)
    qqueue->getat(): Returns an element at the specified position in this queue without removing it.
    Definition qqueue.c:409
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qqueue.c Queue implementation.
    +
    31  *
    +
    32  * qqueue container is a queue implementation. It represents a
    +
    33  * first-in-first-out(FIFO). It extends qlist container that allow a
    +
    34  * linked-list to be treated as a queue.
    +
    35  *
    +
    36  * @code
    +
    37  * [Conceptional Data Structure Diagram]
    +
    38  *
    +
    39  * top bottom
    +
    40  * DATA POP <== [ A ][ B ][ C ] <== DATA PUSH
    +
    41  * (positive index) 0 1 2
    +
    42  * (negative index) -3 -2 -1
    +
    43  * @endcode
    +
    44  *
    +
    45  * @code
    +
    46  * // create queue
    +
    47  * qqueue_t *queue = qQueue();
    +
    48  *
    +
    49  * // example: integer queue
    +
    50  * queue->pushint(queue, 1);
    +
    51  * queue->pushint(queue, 2);
    +
    52  * queue->pushint(queue, 3);
    +
    53  *
    +
    54  * printf("popint(): %d\n", queue->popint(queue));
    +
    55  * printf("popint(): %d\n", queue->popint(queue));
    +
    56  * printf("popint(): %d\n", queue->popint(queue));
    +
    57  *
    +
    58  * // example: string queue
    +
    59  * queue->pushstr(queue, "A string");
    +
    60  * queue->pushstr(queue, "B string");
    +
    61  * queue->pushstr(queue, "C string");
    +
    62  *
    +
    63  * char *str = queue->popstr(queue);
    +
    64  * printf("popstr(): %s\n", str);
    +
    65  * free(str);
    +
    66  * str = queue->popstr(queue);
    +
    67  * printf("popstr(): %s\n", str);
    +
    68  * free(str);
    +
    69  * str = queue->popstr(queue);
    +
    70  * printf("popstr(): %s\n", str);
    +
    71  * free(str);
    +
    72  *
    +
    73  * // example: object queue
    +
    74  * queue->push(queue, "A object", sizeof("A object"));
    +
    75  * queue->push(queue, "B object", sizeof("B object"));
    +
    76  * queue->push(queue, "C object", sizeof("C object"));
    +
    77  *
    +
    78  * void *obj = queue->pop(queue, NULL);
    +
    79  * printf("pop(): %s\n", (char*)obj);
    +
    80  * free(obj);
    +
    81  * obj = queue->pop(queue, NULL);
    +
    82  * printf("pop(): %s\n", (char*)obj);
    +
    83  * free(obj);
    +
    84  * obj = queue->pop(queue, NULL);
    +
    85  * printf("pop(): %s\n", (char*)obj);
    +
    86  * free(obj);
    +
    87  *
    +
    88  * // release
    +
    89  * queue->free(queue);
    +
    90  *
    +
    91  * [Output]
    +
    92  * popint(): 3
    +
    93  * popint(): 2
    +
    94  * popint(): 1
    +
    95  * popstr(): C string
    +
    96  * popstr(): B string
    +
    97  * popstr(): A string
    +
    98  * pop(): C object
    +
    99  * pop(): B object
    +
    100  * pop(): A object
    +
    101  * @endcode
    +
    102  */
    +
    103 
    +
    104 #include <stdio.h>
    +
    105 #include <stdlib.h>
    +
    106 #include <stdbool.h>
    +
    107 #include <string.h>
    +
    108 #include <errno.h>
    +
    109 #include "qinternal.h"
    +
    110 #include "containers/qqueue.h"
    +
    111 
    +
    112 /**
    +
    113  * Create new queue container
    +
    114  *
    +
    115  * @param options combination of initialization options.
    +
    116  *
    +
    117  * @return a pointer of malloced qqueue container, otherwise returns NULL.
    +
    118  * @retval errno will be set in error condition.
    +
    119  * - ENOMEM : Memory allocation failure.
    +
    120  *
    +
    121  * @code
    +
    122  * qqueue_t *queue = qQueue(QQUEUE_THREADSAFE);
    +
    123  * @endcode
    +
    124  *
    +
    125  * @note
    +
    126  * Available options:
    +
    127  * - QQUEUE_THREADSAFE - make it thread-safe.
    +
    128  */
    +
    129 qqueue_t *qqueue(int options) {
    +
    130  qqueue_t *queue = (qqueue_t *) malloc(sizeof(qqueue_t));
    +
    131  if (queue == NULL) {
    +
    132  errno = ENOMEM;
    +
    133  return NULL;
    +
    134  }
    +
    135 
    +
    136  memset((void *) queue, 0, sizeof(qqueue_t));
    +
    137  queue->list = qlist(options);
    +
    138  if (queue->list == NULL) {
    +
    139  free(queue);
    +
    140  return NULL;
    +
    141  }
    +
    142 
    +
    143  // methods
    +
    144  queue->setsize = qqueue_setsize;
    +
    145 
    +
    146  queue->push = qqueue_push;
    +
    147  queue->pushstr = qqueue_pushstr;
    +
    148  queue->pushint = qqueue_pushint;
    +
    149 
    +
    150  queue->pop = qqueue_pop;
    +
    151  queue->popstr = qqueue_popstr;
    +
    152  queue->popint = qqueue_popint;
    +
    153  queue->popat = qqueue_popat;
    +
    154 
    +
    155  queue->get = qqueue_get;
    +
    156  queue->getstr = qqueue_getstr;
    +
    157  queue->getint = qqueue_getint;
    +
    158  queue->getat = qqueue_getat;
    +
    159 
    +
    160  queue->size = qqueue_size;
    +
    161  queue->clear = qqueue_clear;
    +
    162  queue->debug = qqueue_debug;
    +
    163  queue->free = qqueue_free;
    +
    164 
    +
    165  return queue;
    +
    166 }
    +
    167 
    +
    168 /**
    +
    169  * qqueue->setsize(): Sets maximum number of elements allowed in this
    +
    170  * queue.
    +
    171  *
    +
    172  * @param queue qqueue container pointer.
    +
    173  * @param max maximum number of elements. 0 means no limit.
    +
    174  *
    +
    175  * @return previous maximum number.
    +
    176  */
    +
    177 size_t qqueue_setsize(qqueue_t *queue, size_t max) {
    +
    178  return queue->list->setsize(queue->list, max);
    +
    179 }
    +
    180 
    +
    181 /**
    +
    182  * qqueue->push(): Pushes an element onto the top of this queue.
    +
    183  *
    +
    184  * @param queue qqueue container pointer.
    +
    185  * @param data a pointer which points data memory.
    +
    186  * @param size size of the data.
    +
    187  *
    +
    188  * @return true if successful, otherwise returns false.
    +
    189  * @retval errno will be set in error condition.
    +
    190  * - EINVAL : Invalid argument.
    +
    191  * - ENOBUFS : Queue full. Only happens when this queue has set to have
    +
    192  * limited number of elements)
    +
    193  * - ENOMEM : Memory allocation failure.
    +
    194  */
    +
    195 bool qqueue_push(qqueue_t *queue, const void *data, size_t size) {
    +
    196  return queue->list->addlast(queue->list, data, size);
    +
    197 }
    +
    198 
    +
    199 /**
    +
    200  * qqueue->pushstr(): Pushes a string onto the top of this queue.
    +
    201  *
    +
    202  * @param queue qqueue container pointer.
    +
    203  * @param data a pointer which points data memory.
    +
    204  * @param size size of the data.
    +
    205  *
    +
    206  * @return true if successful, otherwise returns false.
    +
    207  * @retval errno will be set in error condition.
    +
    208  * - EINVAL : Invalid argument.
    +
    209  * - ENOBUFS : Queue full. Only happens when this queue has set to have
    +
    210  * limited number of elements.
    +
    211  * - ENOMEM : Memory allocation failure.
    +
    212  */
    +
    213 bool qqueue_pushstr(qqueue_t *queue, const char *str) {
    +
    214  if (str == NULL) {
    +
    215  errno = EINVAL;
    +
    216  return false;
    +
    217  }
    +
    218  return queue->list->addlast(queue->list, str, strlen(str) + 1);
    +
    219 }
    +
    220 
    +
    221 /**
    +
    222  * qqueue->pushint(): Pushes a integer onto the top of this queue.
    +
    223  *
    +
    224  * @param queue qqueue container pointer.
    +
    225  * @param num integer data.
    +
    226  *
    +
    227  * @return true if successful, otherwise returns false.
    +
    228  * @retval errno will be set in error condition.
    +
    229  * - ENOBUFS : Queue full. Only happens when this queue has set to have
    +
    230  * limited number of elements.
    +
    231  * - ENOMEM : Memory allocation failure.
    +
    232  */
    +
    233 bool qqueue_pushint(qqueue_t *queue, int64_t num) {
    +
    234  return queue->list->addlast(queue->list, &num, sizeof(num));
    +
    235 }
    +
    236 
    +
    237 /**
    +
    238  * qqueue->pop(): Removes a element at the top of this queue and returns
    +
    239  * that element.
    +
    240  *
    +
    241  * @param queue qqueue container pointer.
    +
    242  * @param size if size is not NULL, element size will be stored.
    +
    243  *
    +
    244  * @return a pointer of malloced element, otherwise returns NULL.
    +
    245  * @retval errno will be set in error condition.
    +
    246  * - ENOENT : Queue is empty.
    +
    247  * - ENOMEM : Memory allocation failure.
    +
    248  */
    +
    249 void *qqueue_pop(qqueue_t *queue, size_t *size) {
    +
    250  return queue->list->popfirst(queue->list, size);
    +
    251 }
    +
    252 
    +
    253 /**
    +
    254  * qqueue->popstr(): Removes a element at the top of this queue and
    +
    255  * returns that element.
    +
    256  *
    +
    257  * @param queue qqueue container pointer.
    +
    258  *
    +
    259  * @return a pointer of malloced string element, otherwise returns NULL.
    +
    260  * @retval errno will be set in error condition.
    +
    261  * - ENOENT : Queue is empty.
    +
    262  * - ENOMEM : Memory allocation failure.
    +
    263  *
    +
    264  * @note
    +
    265  * The string element should be pushed through pushstr().
    +
    266  */
    +
    267 char *qqueue_popstr(qqueue_t *queue) {
    +
    268  size_t strsize;
    +
    269  char *str = queue->list->popfirst(queue->list, &strsize);
    +
    270  if (str != NULL) {
    +
    271  str[strsize - 1] = '\0'; // just to make sure
    +
    272  }
    +
    273 
    +
    274  return str;
    +
    275 }
    +
    276 
    +
    277 /**
    +
    278  * qqueue->popint(): Removes a integer at the top of this queue and
    +
    279  * returns that element.
    +
    280  *
    +
    281  * @param queue qqueue container pointer.
    +
    282  *
    +
    283  * @return an integer value, otherwise returns 0.
    +
    284  * @retval errno will be set in error condition.
    +
    285  * - ENOENT : Queue is empty.
    +
    286  * - ENOMEM : Memory allocation failure.
    +
    287  *
    +
    288  * @note
    +
    289  * The integer element should be pushed through pushint().
    +
    290  */
    +
    291 int64_t qqueue_popint(qqueue_t *queue) {
    +
    292  int64_t num = 0;
    +
    293  int64_t *pnum = queue->list->popfirst(queue->list, NULL);
    +
    294  if (pnum != NULL) {
    +
    295  num = *pnum;
    +
    296  free(pnum);
    +
    297  }
    +
    298 
    +
    299  return num;
    +
    300 }
    +
    301 
    +
    302 /**
    +
    303  * qqueue->popat(): Returns and remove the element at the specified
    +
    304  * position in this queue.
    +
    305  *
    +
    306  * @param queue qqueue container pointer.
    +
    307  * @param index index at which the specified element is to be inserted
    +
    308  * @param size if size is not NULL, element size will be stored.
    +
    309  *
    +
    310  * @return a pointer of malloced element, otherwise returns NULL.
    +
    311  * @retval errno will be set in error condition.
    +
    312  * - ERANGE : Index out of range.
    +
    313  * - ENOMEM : Memory allocation failure.
    +
    314  *
    +
    315  * @note
    +
    316  * Negative index can be used for addressing a element from the bottom in this
    +
    317  * queue. For example, index -1 will always pop a element which is pushed at
    +
    318  * very last time.
    +
    319  */
    +
    320 void *qqueue_popat(qqueue_t *queue, int index, size_t *size) {
    +
    321  return queue->list->popat(queue->list, index, size);
    +
    322 }
    +
    323 
    +
    324 /**
    +
    325  * qqueue->get(): Returns an element at the top of this queue without
    +
    326  * removing it.
    +
    327  *
    +
    328  * @param queue qqueue container pointer.
    +
    329  * @param size if size is not NULL, element size will be stored.
    +
    330  * @param newmem whether or not to allocate memory for the element.
    +
    331  *
    +
    332  * @return a pointer of malloced element, otherwise returns NULL.
    +
    333  * @retval errno will be set in error condition.
    +
    334  * - ENOENT : Queue is empty.
    +
    335  * - ENOMEM : Memory allocation failure.
    +
    336  */
    +
    337 void *qqueue_get(qqueue_t *queue, size_t *size, bool newmem) {
    +
    338  return queue->list->getfirst(queue->list, size, newmem);
    +
    339 }
    +
    340 
    +
    341 /**
    +
    342  * qqueue->getstr(): Returns an string at the top of this queue without
    +
    343  * removing it.
    +
    344  *
    +
    345  * @param queue qqueue container pointer.
    +
    346  *
    +
    347  * @return a pointer of malloced string element, otherwise returns NULL.
    +
    348  * @retval errno will be set in error condition.
    +
    349  * - ENOENT : Queue is empty.
    +
    350  * - ENOMEM : Memory allocation failure.
    +
    351  *
    +
    352  * @note
    +
    353  * The string element should be pushed through pushstr().
    +
    354  */
    +
    355 char *qqueue_getstr(qqueue_t *queue) {
    +
    356  size_t strsize;
    +
    357  char *str = queue->list->getfirst(queue->list, &strsize, true);
    +
    358  if (str != NULL) {
    +
    359  str[strsize - 1] = '\0'; // just to make sure
    +
    360  }
    +
    361 
    +
    362  return str;
    +
    363 }
    +
    364 
    +
    365 /**
    +
    366  * qqueue->getint(): Returns an integer at the top of this queue without
    +
    367  * removing it.
    +
    368  *
    +
    369  * @param queue qqueue container pointer.
    +
    370  *
    +
    371  * @return an integer value, otherwise returns 0.
    +
    372  * @retval errno will be set in error condition.
    +
    373  * - ENOENT : Queue is empty.
    +
    374  * - ENOMEM : Memory allocation failure.
    +
    375  *
    +
    376  * @note
    +
    377  * The integer element should be pushed through pushint().
    +
    378  */
    +
    379 int64_t qqueue_getint(qqueue_t *queue) {
    +
    380  int64_t num = 0;
    +
    381  int64_t *pnum = queue->list->getfirst(queue->list, NULL, true);
    +
    382  if (pnum != NULL) {
    +
    383  num = *pnum;
    +
    384  free(pnum);
    +
    385  }
    +
    386 
    +
    387  return num;
    +
    388 }
    +
    389 
    +
    390 /**
    +
    391  * qqueue->getat(): Returns an element at the specified position in this
    +
    392  * queue without removing it.
    +
    393  *
    +
    394  * @param queue qqueue container pointer.
    +
    395  * @param index index at which the specified element is to be inserted
    +
    396  * @param size if size is not NULL, element size will be stored.
    +
    397  * @param newmem whether or not to allocate memory for the element.
    +
    398  *
    +
    399  * @return a pointer of element, otherwise returns NULL.
    +
    400  * @retval errno will be set in error condition.
    +
    401  * - ERANGE : Index out of range.
    +
    402  * - ENOMEM : Memory allocation failure.
    +
    403  *
    +
    404  * @note
    +
    405  * Negative index can be used for addressing a element from the bottom in this
    +
    406  * queue. For example, index -1 will always get a element which is pushed at
    +
    407  * very last time.
    +
    408  */
    +
    409 void *qqueue_getat(qqueue_t *queue, int index, size_t *size, bool newmem) {
    +
    410  return queue->list->getat(queue->list, index, size, newmem);
    +
    411 }
    +
    412 
    +
    413 /**
    +
    414  * qqueue->size(): Returns the number of elements in this queue.
    +
    415  *
    +
    416  * @param queue qqueue container pointer.
    +
    417  *
    +
    418  * @return the number of elements in this queue.
    +
    419  */
    +
    420 size_t qqueue_size(qqueue_t *queue) {
    +
    421  return queue->list->size(queue->list);
    +
    422 }
    +
    423 
    +
    424 /**
    +
    425  * qqueue->clear(): Removes all of the elements from this queue.
    +
    426  *
    +
    427  * @param queue qqueue container pointer.
    +
    428  */
    +
    429 void qqueue_clear(qqueue_t *queue) {
    +
    430  queue->list->clear(queue->list);
    +
    431 }
    +
    432 
    +
    433 /**
    +
    434  * qqueue->debug(): Print out stored elements for debugging purpose.
    +
    435  *
    +
    436  * @param queue qqueue container pointer.
    +
    437  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    438  *
    +
    439  * @return true if successful, otherwise returns false.
    +
    440  */
    +
    441 bool qqueue_debug(qqueue_t *queue, FILE *out) {
    +
    442  return queue->list->debug(queue->list, out);
    +
    443 }
    +
    444 
    +
    445 /**
    +
    446  * qqueue->free(): Free qqueue_t
    +
    447  *
    +
    448  * @param queue qqueue container pointer.
    +
    449  *
    +
    450  * @return always returns true.
    +
    451  */
    +
    452 void qqueue_free(qqueue_t *queue) {
    +
    453  queue->list->free(queue->list);
    +
    454  free(queue);
    +
    455 }
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124
    +
    int64_t qqueue_getint(qqueue_t *queue)
    qqueue->getint(): Returns an integer at the top of this queue without removing it.
    Definition: qqueue.c:379
    +
    void * qqueue_get(qqueue_t *queue, size_t *size, bool newmem)
    qqueue->get(): Returns an element at the top of this queue without removing it.
    Definition: qqueue.c:337
    +
    bool qqueue_push(qqueue_t *queue, const void *data, size_t size)
    qqueue->push(): Pushes an element onto the top of this queue.
    Definition: qqueue.c:195
    +
    bool qqueue_pushstr(qqueue_t *queue, const char *str)
    qqueue->pushstr(): Pushes a string onto the top of this queue.
    Definition: qqueue.c:213
    +
    bool qqueue_debug(qqueue_t *queue, FILE *out)
    qqueue->debug(): Print out stored elements for debugging purpose.
    Definition: qqueue.c:441
    +
    void qqueue_clear(qqueue_t *queue)
    qqueue->clear(): Removes all of the elements from this queue.
    Definition: qqueue.c:429
    +
    void * qqueue_popat(qqueue_t *queue, int index, size_t *size)
    qqueue->popat(): Returns and remove the element at the specified position in this queue.
    Definition: qqueue.c:320
    +
    size_t qqueue_size(qqueue_t *queue)
    qqueue->size(): Returns the number of elements in this queue.
    Definition: qqueue.c:420
    +
    void qqueue_free(qqueue_t *queue)
    qqueue->free(): Free qqueue_t
    Definition: qqueue.c:452
    +
    qqueue_t * qqueue(int options)
    Create new queue container.
    Definition: qqueue.c:129
    +
    void * qqueue_pop(qqueue_t *queue, size_t *size)
    qqueue->pop(): Removes a element at the top of this queue and returns that element.
    Definition: qqueue.c:249
    +
    void * qqueue_getat(qqueue_t *queue, int index, size_t *size, bool newmem)
    qqueue->getat(): Returns an element at the specified position in this queue without removing it.
    Definition: qqueue.c:409
    +
    char * qqueue_popstr(qqueue_t *queue)
    qqueue->popstr(): Removes a element at the top of this queue and returns that element.
    Definition: qqueue.c:267
    +
    bool qqueue_pushint(qqueue_t *queue, int64_t num)
    qqueue->pushint(): Pushes a integer onto the top of this queue.
    Definition: qqueue.c:233
    +
    char * qqueue_getstr(qqueue_t *queue)
    qqueue->getstr(): Returns an string at the top of this queue without removing it.
    Definition: qqueue.c:355
    +
    size_t qqueue_setsize(qqueue_t *queue, size_t max)
    qqueue->setsize(): Sets maximum number of elements allowed in this queue.
    Definition: qqueue.c:177
    +
    int64_t qqueue_popint(qqueue_t *queue)
    qqueue->popint(): Removes a integer at the top of this queue and returns that element.
    Definition: qqueue.c:291
    diff --git a/doc/html/qsem_8c.html b/doc/html/qsem_8c.html index 9078a0b7..fe004b09 100644 --- a/doc/html/qsem_8c.html +++ b/doc/html/qsem_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: ipc/qsem.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qsem.c File Reference
    +
    +
    qsem.c File Reference
    @@ -70,31 +70,31 @@

    Go to the source code of this file.

    - - + - + - + - + - + - + - + - +

    +

    Functions

    int qsem_init (const char *keyfile, int keyid, int nsems, bool recreate)
     Initialize semaphore.
     Initialize semaphore. More...
     
    int qsem_getid (const char *keyfile, int keyid)
     Get semaphore identifier by keyfile and keyid for the existing semaphore.
     Get semaphore identifier by keyfile and keyid for the existing semaphore. More...
     
    bool qsem_enter (int semid, int semno)
     Turn on the flag of semaphore then entering critical section.
     Turn on the flag of semaphore then entering critical section. More...
     
    bool qsem_enter_nowait (int semid, int semno)
     Try to turn on the flag of semaphore.
     Try to turn on the flag of semaphore. More...
     
    bool qsem_enter_force (int semid, int semno, int maxwaitms, bool *forceflag)
     Force to turn on the flag of semaphore.
     Force to turn on the flag of semaphore. More...
     
    bool qsem_leave (int semid, int semno)
     Turn off the flag of semaphore then leaving critical section.
     Turn off the flag of semaphore then leaving critical section. More...
     
    bool qsem_check (int semid, int semno)
     Get the status of semaphore.
     Get the status of semaphore. More...
     
    bool qsem_free (int semid)
     Release semaphore to system.
     Release semaphore to system. More...
     

    Detailed Description

    @@ -103,7 +103,7 @@
    #define MAX_SEMAPHORES (2)
    // create semaphores
    -
    int semid = qsem_init("/some/file/for/generating/unique/key", 'q', MAX_SEMAPHORES, true);
    +
    int semid = qsem_init("/some/file/for/generating/unique/key", 'q', MAX_SEMAPHORES, true);
    if(semid < 0) {
    printf("ERROR: Can't initialize semaphores.\n");
    return -1;
    @@ -113,43 +113,43 @@
    (... child forking codes ...)
    // at the end of daemon, free semaphores
    -
    if(semid >= 0) qsem_free(semid);
    +
    if(semid >= 0) qsem_free(semid);
    [forked child]
    // critical section for resource 0
    -
    qsem_enter(semid, 0);
    +
    qsem_enter(semid, 0);
    (... guaranteed as atomic procedure ...)
    -
    qsem_leave(semid, 0);
    +
    qsem_leave(semid, 0);
    (... some codes ...)
    // critical section for resource 1
    -
    qsem_enter(semid, 1);
    +
    qsem_enter(semid, 1);
    (... guaranteed as atomic procedure ...)
    -
    qsem_leave(semid, 1);
    +
    qsem_leave(semid, 1);
    [other program which uses resource 1]
    -
    int semid = qsem_getid("/some/file/for/generating/unique/key", 'q');
    +
    int semid = qsem_getid("/some/file/for/generating/unique/key", 'q');
    if(semid < 0) {
    printf("ERROR: Can't get semaphore id.\n");
    return -1;
    }
    // critical section for resource 1
    -
    qsem_enter(semid, 1);
    +
    qsem_enter(semid, 1);
    (... guaranteed as atomic procedure ...)
    -
    qsem_leave(semid, 1);
    -
    bool qsem_leave(int semid, int semno)
    Turn off the flag of semaphore then leaving critical section.
    Definition qsem.c:263
    -
    int qsem_getid(const char *keyfile, int keyid)
    Get semaphore identifier by keyfile and keyid for the existing semaphore.
    Definition qsem.c:155
    -
    bool qsem_free(int semid)
    Release semaphore to system.
    Definition qsem.c:298
    -
    int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
    Initialize semaphore.
    Definition qsem.c:102
    -
    bool qsem_enter(int semid, int semno)
    Turn on the flag of semaphore then entering critical section.
    Definition qsem.c:180
    +
    qsem_leave(semid, 1);
    +
    bool qsem_leave(int semid, int semno)
    Turn off the flag of semaphore then leaving critical section.
    Definition: qsem.c:263
    +
    int qsem_getid(const char *keyfile, int keyid)
    Get semaphore identifier by keyfile and keyid for the existing semaphore.
    Definition: qsem.c:155
    +
    bool qsem_free(int semid)
    Release semaphore to system.
    Definition: qsem.c:298
    +
    int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
    Initialize semaphore.
    Definition: qsem.c:102
    +
    bool qsem_enter(int semid, int semno)
    Turn on the flag of semaphore then entering critical section.
    Definition: qsem.c:180

    Definition in file qsem.c.

    Function Documentation

    - -

    ◆ qsem_init()

    + +

    ◆ qsem_init()

    @@ -197,14 +197,14 @@

    Returns
    non-negative shared memory identifier if successful, otherwise returns -1
    -
    int semid = qsem_init("/tmp/mydaemon.pid", 'q', 10, true);
    +
    int semid = qsem_init("/tmp/mydaemon.pid", 'q', 10, true);

    Definition at line 102 of file qsem.c.

    - -

    ◆ qsem_getid()

    + +

    ◆ qsem_getid()

    @@ -243,8 +243,8 @@

    -

    ◆ qsem_enter()

    + +

    ◆ qsem_enter()

    @@ -284,8 +284,8 @@

    -

    ◆ qsem_enter_nowait()

    + +

    ◆ qsem_enter_nowait()

    @@ -325,8 +325,8 @@

    -

    ◆ qsem_enter_force()

    + +

    ◆ qsem_enter_force()

    @@ -380,8 +380,8 @@

    -

    ◆ qsem_leave()

    + +

    ◆ qsem_leave()

    @@ -420,8 +420,8 @@

    -

    ◆ qsem_check()

    + +

    ◆ qsem_check()

    @@ -460,8 +460,8 @@

    -

    ◆ qsem_free()

    + +

    ◆ qsem_free()

    @@ -495,7 +495,7 @@

      - +

    diff --git a/doc/html/qsem_8c_source.html b/doc/html/qsem_8c_source.html index ac42a66d..be320c7d 100644 --- a/doc/html/qsem_8c_source.html +++ b/doc/html/qsem_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: ipc/qsem.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,336 +52,337 @@

    -
    qsem.c
    +
    +
    qsem.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qsem.c Semaphore APIs.
    -
    31 *
    -
    32 * @note
    -
    33 * @code
    -
    34 * [daemon main]
    -
    35 * #define MAX_SEMAPHORES (2)
    -
    36 *
    -
    37 * // create semaphores
    -
    38 * int semid = qsem_init("/some/file/for/generating/unique/key", 'q', MAX_SEMAPHORES, true);
    -
    39 * if(semid < 0) {
    -
    40 * printf("ERROR: Can't initialize semaphores.\n");
    -
    41 * return -1;
    -
    42 * }
    -
    43 *
    -
    44 * // fork childs
    -
    45 * (... child forking codes ...)
    -
    46 *
    -
    47 * // at the end of daemon, free semaphores
    -
    48 * if(semid >= 0) qsem_free(semid);
    -
    49 *
    -
    50 * [forked child]
    -
    51 * // critical section for resource 0
    -
    52 * qsem_enter(semid, 0);
    -
    53 * (... guaranteed as atomic procedure ...)
    -
    54 * qsem_leave(semid, 0);
    -
    55 *
    -
    56 * (... some codes ...)
    -
    57 *
    -
    58 * // critical section for resource 1
    -
    59 * qsem_enter(semid, 1);
    -
    60 * (... guaranteed as atomic procedure ...)
    -
    61 * qsem_leave(semid, 1);
    -
    62 *
    -
    63 * [other program which uses resource 1]
    -
    64 * int semid = qsem_getid("/some/file/for/generating/unique/key", 'q');
    -
    65 * if(semid < 0) {
    -
    66 * printf("ERROR: Can't get semaphore id.\n");
    -
    67 * return -1;
    -
    68 * }
    -
    69 *
    -
    70 * // critical section for resource 1
    -
    71 * qsem_enter(semid, 1);
    -
    72 * (... guaranteed as atomic procedure ...)
    -
    73 * qsem_leave(semid, 1);
    -
    74 *
    -
    75 * @endcode
    -
    76 */
    -
    77
    -
    78#ifndef DISABLE_IPC
    -
    79
    -
    80#include <stdio.h>
    -
    81#include <stdlib.h>
    -
    82#include <stdbool.h>
    -
    83#include <unistd.h>
    -
    84#include <sys/sem.h>
    -
    85#include "qinternal.h"
    -
    86#include "ipc/qsem.h"
    -
    87
    -
    88/**
    -
    89 * Initialize semaphore
    -
    90 *
    -
    91 * @param keyfile seed for generating unique IPC key
    -
    92 * @param keyid seed for generating unique IPC key
    -
    93 * @param nsems number of semaphores to initialize
    -
    94 * @param recreate set to true to re-create semaphore if exists
    -
    95 *
    -
    96 * @return non-negative shared memory identifier if successful, otherwise returns -1
    -
    97 *
    -
    98 * @code
    -
    99 * int semid = qsem_init("/tmp/mydaemon.pid", 'q', 10, true);
    -
    100 * @endcode
    -
    101 */
    -
    102int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate) {
    -
    103 key_t semkey;
    -
    104 int semid;
    -
    105
    -
    106 // generate unique key using ftok();
    -
    107 if (keyfile != NULL) {
    -
    108 semkey = ftok(keyfile, keyid);
    -
    109 if (semkey == -1)
    -
    110 return -1;
    -
    111 } else {
    -
    112 semkey = IPC_PRIVATE;
    -
    113 }
    -
    114
    -
    115 // create semaphores
    -
    116 if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
    -
    117 if (recreate == false)
    -
    118 return -1;
    -
    119
    -
    120 // destroy & re-create
    -
    121 if ((semid = qsem_getid(keyfile, keyid)) >= 0)
    -
    122 qsem_free(semid);
    -
    123 if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1)
    -
    124 return -1;
    -
    125 }
    -
    126
    -
    127 // initializing
    -
    128 int i;
    -
    129 for (i = 0; i < nsems; i++) {
    -
    130 struct sembuf sbuf;
    -
    131
    -
    132 /* set sbuf */
    -
    133 sbuf.sem_num = i;
    -
    134 sbuf.sem_op = 1;
    -
    135 sbuf.sem_flg = 0;
    -
    136
    -
    137 /* initialize */
    -
    138 if (semop(semid, &sbuf, 1) != 0) {
    -
    139 qsem_free(semid);
    -
    140 return -1;
    -
    141 }
    -
    142 }
    -
    143
    -
    144 return semid;
    -
    145}
    -
    146
    -
    147/**
    -
    148 * Get semaphore identifier by keyfile and keyid for the existing semaphore
    -
    149 *
    -
    150 * @param keyfile seed for generating unique IPC key
    -
    151 * @param keyid seed for generating unique IPC key
    -
    152 *
    -
    153 * @return non-negative shared memory identifier if successful, otherwise returns -1
    -
    154 */
    -
    155int qsem_getid(const char *keyfile, int keyid) {
    -
    156 int semid;
    -
    157
    -
    158 /* generate unique key using ftok() */
    -
    159 key_t semkey = ftok(keyfile, keyid);
    -
    160 if (semkey == -1)
    -
    161 return -1;
    -
    162
    -
    163 /* get current semaphore id */
    -
    164 if ((semid = semget(semkey, 0, 0)) == -1)
    -
    165 return -1;
    -
    166
    -
    167 return semid;
    -
    168}
    -
    169
    -
    170/**
    -
    171 * Turn on the flag of semaphore then entering critical section
    -
    172 *
    -
    173 * @param semid semaphore identifier
    -
    174 * @param semno semaphore number
    -
    175 *
    -
    176 * @return true if successful, otherwise returns false
    -
    177 *
    -
    178 * @note If the semaphore is already turned on, this will wait until released
    -
    179 */
    -
    180bool qsem_enter(int semid, int semno) {
    -
    181 struct sembuf sbuf;
    -
    182
    -
    183 /* set sbuf */
    -
    184 sbuf.sem_num = semno;
    -
    185 sbuf.sem_op = -1;
    -
    186 sbuf.sem_flg = SEM_UNDO;
    -
    187
    -
    188 /* lock */
    -
    189 if (semop(semid, &sbuf, 1) != 0)
    -
    190 return false;
    -
    191 return true;
    -
    192}
    -
    193
    -
    194/**
    -
    195 * Try to turn on the flag of semaphore. If it is already turned on, do not wait.
    -
    196 *
    -
    197 * @param semid semaphore identifier
    -
    198 * @param semno semaphore number
    -
    199 *
    -
    200 * @return true if successful, otherwise(already turned on by other) returns false
    -
    201 */
    -
    202bool qsem_enter_nowait(int semid, int semno) {
    -
    203 struct sembuf sbuf;
    -
    204
    -
    205 /* set sbuf */
    -
    206 sbuf.sem_num = semno;
    -
    207 sbuf.sem_op = -1;
    -
    208 sbuf.sem_flg = SEM_UNDO | IPC_NOWAIT;
    -
    209
    -
    210 /* lock */
    -
    211 if (semop(semid, &sbuf, 1) != 0)
    -
    212 return false;
    -
    213 return true;
    -
    214}
    -
    215
    -
    216/**
    -
    217 * Force to turn on the flag of semaphore.
    -
    218 *
    -
    219 * @param semid semaphore identifier
    -
    220 * @param semno semaphore number
    -
    221 * @param maxwaitms maximum waiting micro-seconds to release
    -
    222 * @param forceflag status will be stored, it can be NULL if you don't need this information
    -
    223 *
    -
    224 * @return true if successful, otherwise returns false
    -
    225 *
    -
    226 * @note
    -
    227 * This will wait the semaphore to be released with in maxwaitms.
    -
    228 * If it it released by locker normally with in maxwaitms, forceflag will be set to false.
    -
    229 * But if maximum maxwaitms is exceed and the semaphore is released forcely, forceflag will
    -
    230 * be set to true.
    -
    231 */
    -
    232bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag) {
    -
    233 int wait;
    -
    234 for (wait = 0; wait < maxwaitms; wait += 10) {
    -
    235 if (qsem_enter_nowait(semid, semno) == true) {
    -
    236 if (forceflag != NULL)
    -
    237 *forceflag = false;
    -
    238 return true;
    -
    239 }
    -
    240 usleep(10 * 1000); // sleep 10ms
    -
    241 }
    -
    242
    -
    243 DEBUG("force to unlock semaphore %d-%d", semid, semno);
    -
    244 while (true) {
    -
    245 qsem_leave(semid, semno);
    -
    246 if (qsem_enter_nowait(semid, semno) == true)
    -
    247 break;
    -
    248 }
    -
    249
    -
    250 if (forceflag != NULL)
    -
    251 *forceflag = true;
    -
    252 return true;
    -
    253}
    -
    254
    -
    255/**
    -
    256 * Turn off the flag of semaphore then leaving critical section
    -
    257 *
    -
    258 * @param semid semaphore identifier
    -
    259 * @param semno semaphore number
    -
    260 *
    -
    261 * @return true if successful, otherwise returns false
    -
    262 */
    -
    263bool qsem_leave(int semid, int semno) {
    -
    264 struct sembuf sbuf;
    -
    265
    -
    266 /* set sbuf */
    -
    267 sbuf.sem_num = semno;
    -
    268 sbuf.sem_op = 1;
    -
    269 sbuf.sem_flg = SEM_UNDO;
    -
    270
    -
    271 /* unlock */
    -
    272 if (semop(semid, &sbuf, 1) != 0)
    -
    273 return false;
    -
    274 return true;
    -
    275}
    -
    276
    -
    277/**
    -
    278 * Get the status of semaphore
    -
    279 *
    -
    280 * @param semid semaphore identifier
    -
    281 * @param semno semaphore number
    -
    282 *
    -
    283 * @return true for the flag on, false for the flag off
    -
    284 */
    -
    285bool qsem_check(int semid, int semno) {
    -
    286 if (semctl(semid, semno, GETVAL, 0) == 0)
    -
    287 return true; // locked
    -
    288 return false; // unlocked
    -
    289}
    -
    290
    -
    291/**
    -
    292 * Release semaphore to system
    -
    293 *
    -
    294 * @param semid semaphore identifier
    -
    295 *
    -
    296 * @return true if successful, otherwise returns false
    -
    297 */
    -
    298bool qsem_free(int semid) {
    -
    299 if (semid < 0)
    -
    300 return false;
    -
    301 if (semctl(semid, 0, IPC_RMID, 0) != 0)
    -
    302 return false;
    -
    303 return true;
    -
    304}
    -
    305
    -
    306#endif /* DISABLE_IPC */
    -
    bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag)
    Force to turn on the flag of semaphore.
    Definition qsem.c:232
    -
    bool qsem_leave(int semid, int semno)
    Turn off the flag of semaphore then leaving critical section.
    Definition qsem.c:263
    -
    bool qsem_enter_nowait(int semid, int semno)
    Try to turn on the flag of semaphore.
    Definition qsem.c:202
    -
    int qsem_getid(const char *keyfile, int keyid)
    Get semaphore identifier by keyfile and keyid for the existing semaphore.
    Definition qsem.c:155
    -
    bool qsem_free(int semid)
    Release semaphore to system.
    Definition qsem.c:298
    -
    bool qsem_check(int semid, int semno)
    Get the status of semaphore.
    Definition qsem.c:285
    -
    int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
    Initialize semaphore.
    Definition qsem.c:102
    -
    bool qsem_enter(int semid, int semno)
    Turn on the flag of semaphore then entering critical section.
    Definition qsem.c:180
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qsem.c Semaphore APIs.
    +
    31  *
    +
    32  * @note
    +
    33  * @code
    +
    34  * [daemon main]
    +
    35  * #define MAX_SEMAPHORES (2)
    +
    36  *
    +
    37  * // create semaphores
    +
    38  * int semid = qsem_init("/some/file/for/generating/unique/key", 'q', MAX_SEMAPHORES, true);
    +
    39  * if(semid < 0) {
    +
    40  * printf("ERROR: Can't initialize semaphores.\n");
    +
    41  * return -1;
    +
    42  * }
    +
    43  *
    +
    44  * // fork childs
    +
    45  * (... child forking codes ...)
    +
    46  *
    +
    47  * // at the end of daemon, free semaphores
    +
    48  * if(semid >= 0) qsem_free(semid);
    +
    49  *
    +
    50  * [forked child]
    +
    51  * // critical section for resource 0
    +
    52  * qsem_enter(semid, 0);
    +
    53  * (... guaranteed as atomic procedure ...)
    +
    54  * qsem_leave(semid, 0);
    +
    55  *
    +
    56  * (... some codes ...)
    +
    57  *
    +
    58  * // critical section for resource 1
    +
    59  * qsem_enter(semid, 1);
    +
    60  * (... guaranteed as atomic procedure ...)
    +
    61  * qsem_leave(semid, 1);
    +
    62  *
    +
    63  * [other program which uses resource 1]
    +
    64  * int semid = qsem_getid("/some/file/for/generating/unique/key", 'q');
    +
    65  * if(semid < 0) {
    +
    66  * printf("ERROR: Can't get semaphore id.\n");
    +
    67  * return -1;
    +
    68  * }
    +
    69  *
    +
    70  * // critical section for resource 1
    +
    71  * qsem_enter(semid, 1);
    +
    72  * (... guaranteed as atomic procedure ...)
    +
    73  * qsem_leave(semid, 1);
    +
    74  *
    +
    75  * @endcode
    +
    76  */
    +
    77 
    +
    78 #ifndef DISABLE_IPC
    +
    79 
    +
    80 #include <stdio.h>
    +
    81 #include <stdlib.h>
    +
    82 #include <stdbool.h>
    +
    83 #include <unistd.h>
    +
    84 #include <sys/sem.h>
    +
    85 #include "qinternal.h"
    +
    86 #include "ipc/qsem.h"
    +
    87 
    +
    88 /**
    +
    89  * Initialize semaphore
    +
    90  *
    +
    91  * @param keyfile seed for generating unique IPC key
    +
    92  * @param keyid seed for generating unique IPC key
    +
    93  * @param nsems number of semaphores to initialize
    +
    94  * @param recreate set to true to re-create semaphore if exists
    +
    95  *
    +
    96  * @return non-negative shared memory identifier if successful, otherwise returns -1
    +
    97  *
    +
    98  * @code
    +
    99  * int semid = qsem_init("/tmp/mydaemon.pid", 'q', 10, true);
    +
    100  * @endcode
    +
    101  */
    +
    102 int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate) {
    +
    103  key_t semkey;
    +
    104  int semid;
    +
    105 
    +
    106  // generate unique key using ftok();
    +
    107  if (keyfile != NULL) {
    +
    108  semkey = ftok(keyfile, keyid);
    +
    109  if (semkey == -1)
    +
    110  return -1;
    +
    111  } else {
    +
    112  semkey = IPC_PRIVATE;
    +
    113  }
    +
    114 
    +
    115  // create semaphores
    +
    116  if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
    +
    117  if (recreate == false)
    +
    118  return -1;
    +
    119 
    +
    120  // destroy & re-create
    +
    121  if ((semid = qsem_getid(keyfile, keyid)) >= 0)
    +
    122  qsem_free(semid);
    +
    123  if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1)
    +
    124  return -1;
    +
    125  }
    +
    126 
    +
    127  // initializing
    +
    128  int i;
    +
    129  for (i = 0; i < nsems; i++) {
    +
    130  struct sembuf sbuf;
    +
    131 
    +
    132  /* set sbuf */
    +
    133  sbuf.sem_num = i;
    +
    134  sbuf.sem_op = 1;
    +
    135  sbuf.sem_flg = 0;
    +
    136 
    +
    137  /* initialize */
    +
    138  if (semop(semid, &sbuf, 1) != 0) {
    +
    139  qsem_free(semid);
    +
    140  return -1;
    +
    141  }
    +
    142  }
    +
    143 
    +
    144  return semid;
    +
    145 }
    +
    146 
    +
    147 /**
    +
    148  * Get semaphore identifier by keyfile and keyid for the existing semaphore
    +
    149  *
    +
    150  * @param keyfile seed for generating unique IPC key
    +
    151  * @param keyid seed for generating unique IPC key
    +
    152  *
    +
    153  * @return non-negative shared memory identifier if successful, otherwise returns -1
    +
    154  */
    +
    155 int qsem_getid(const char *keyfile, int keyid) {
    +
    156  int semid;
    +
    157 
    +
    158  /* generate unique key using ftok() */
    +
    159  key_t semkey = ftok(keyfile, keyid);
    +
    160  if (semkey == -1)
    +
    161  return -1;
    +
    162 
    +
    163  /* get current semaphore id */
    +
    164  if ((semid = semget(semkey, 0, 0)) == -1)
    +
    165  return -1;
    +
    166 
    +
    167  return semid;
    +
    168 }
    +
    169 
    +
    170 /**
    +
    171  * Turn on the flag of semaphore then entering critical section
    +
    172  *
    +
    173  * @param semid semaphore identifier
    +
    174  * @param semno semaphore number
    +
    175  *
    +
    176  * @return true if successful, otherwise returns false
    +
    177  *
    +
    178  * @note If the semaphore is already turned on, this will wait until released
    +
    179  */
    +
    180 bool qsem_enter(int semid, int semno) {
    +
    181  struct sembuf sbuf;
    +
    182 
    +
    183  /* set sbuf */
    +
    184  sbuf.sem_num = semno;
    +
    185  sbuf.sem_op = -1;
    +
    186  sbuf.sem_flg = SEM_UNDO;
    +
    187 
    +
    188  /* lock */
    +
    189  if (semop(semid, &sbuf, 1) != 0)
    +
    190  return false;
    +
    191  return true;
    +
    192 }
    +
    193 
    +
    194 /**
    +
    195  * Try to turn on the flag of semaphore. If it is already turned on, do not wait.
    +
    196  *
    +
    197  * @param semid semaphore identifier
    +
    198  * @param semno semaphore number
    +
    199  *
    +
    200  * @return true if successful, otherwise(already turned on by other) returns false
    +
    201  */
    +
    202 bool qsem_enter_nowait(int semid, int semno) {
    +
    203  struct sembuf sbuf;
    +
    204 
    +
    205  /* set sbuf */
    +
    206  sbuf.sem_num = semno;
    +
    207  sbuf.sem_op = -1;
    +
    208  sbuf.sem_flg = SEM_UNDO | IPC_NOWAIT;
    +
    209 
    +
    210  /* lock */
    +
    211  if (semop(semid, &sbuf, 1) != 0)
    +
    212  return false;
    +
    213  return true;
    +
    214 }
    +
    215 
    +
    216 /**
    +
    217  * Force to turn on the flag of semaphore.
    +
    218  *
    +
    219  * @param semid semaphore identifier
    +
    220  * @param semno semaphore number
    +
    221  * @param maxwaitms maximum waiting micro-seconds to release
    +
    222  * @param forceflag status will be stored, it can be NULL if you don't need this information
    +
    223  *
    +
    224  * @return true if successful, otherwise returns false
    +
    225  *
    +
    226  * @note
    +
    227  * This will wait the semaphore to be released with in maxwaitms.
    +
    228  * If it it released by locker normally with in maxwaitms, forceflag will be set to false.
    +
    229  * But if maximum maxwaitms is exceed and the semaphore is released forcely, forceflag will
    +
    230  * be set to true.
    +
    231  */
    +
    232 bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag) {
    +
    233  int wait;
    +
    234  for (wait = 0; wait < maxwaitms; wait += 10) {
    +
    235  if (qsem_enter_nowait(semid, semno) == true) {
    +
    236  if (forceflag != NULL)
    +
    237  *forceflag = false;
    +
    238  return true;
    +
    239  }
    +
    240  usleep(10 * 1000); // sleep 10ms
    +
    241  }
    +
    242 
    +
    243  DEBUG("force to unlock semaphore %d-%d", semid, semno);
    +
    244  while (true) {
    +
    245  qsem_leave(semid, semno);
    +
    246  if (qsem_enter_nowait(semid, semno) == true)
    +
    247  break;
    +
    248  }
    +
    249 
    +
    250  if (forceflag != NULL)
    +
    251  *forceflag = true;
    +
    252  return true;
    +
    253 }
    +
    254 
    +
    255 /**
    +
    256  * Turn off the flag of semaphore then leaving critical section
    +
    257  *
    +
    258  * @param semid semaphore identifier
    +
    259  * @param semno semaphore number
    +
    260  *
    +
    261  * @return true if successful, otherwise returns false
    +
    262  */
    +
    263 bool qsem_leave(int semid, int semno) {
    +
    264  struct sembuf sbuf;
    +
    265 
    +
    266  /* set sbuf */
    +
    267  sbuf.sem_num = semno;
    +
    268  sbuf.sem_op = 1;
    +
    269  sbuf.sem_flg = SEM_UNDO;
    +
    270 
    +
    271  /* unlock */
    +
    272  if (semop(semid, &sbuf, 1) != 0)
    +
    273  return false;
    +
    274  return true;
    +
    275 }
    +
    276 
    +
    277 /**
    +
    278  * Get the status of semaphore
    +
    279  *
    +
    280  * @param semid semaphore identifier
    +
    281  * @param semno semaphore number
    +
    282  *
    +
    283  * @return true for the flag on, false for the flag off
    +
    284  */
    +
    285 bool qsem_check(int semid, int semno) {
    +
    286  if (semctl(semid, semno, GETVAL, 0) == 0)
    +
    287  return true; // locked
    +
    288  return false; // unlocked
    +
    289 }
    +
    290 
    +
    291 /**
    +
    292  * Release semaphore to system
    +
    293  *
    +
    294  * @param semid semaphore identifier
    +
    295  *
    +
    296  * @return true if successful, otherwise returns false
    +
    297  */
    +
    298 bool qsem_free(int semid) {
    +
    299  if (semid < 0)
    +
    300  return false;
    +
    301  if (semctl(semid, 0, IPC_RMID, 0) != 0)
    +
    302  return false;
    +
    303  return true;
    +
    304 }
    +
    305 
    +
    306 #endif /* DISABLE_IPC */
    +
    bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag)
    Force to turn on the flag of semaphore.
    Definition: qsem.c:232
    +
    bool qsem_leave(int semid, int semno)
    Turn off the flag of semaphore then leaving critical section.
    Definition: qsem.c:263
    +
    bool qsem_enter_nowait(int semid, int semno)
    Try to turn on the flag of semaphore.
    Definition: qsem.c:202
    +
    int qsem_getid(const char *keyfile, int keyid)
    Get semaphore identifier by keyfile and keyid for the existing semaphore.
    Definition: qsem.c:155
    +
    bool qsem_free(int semid)
    Release semaphore to system.
    Definition: qsem.c:298
    +
    bool qsem_check(int semid, int semno)
    Get the status of semaphore.
    Definition: qsem.c:285
    +
    int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
    Initialize semaphore.
    Definition: qsem.c:102
    +
    bool qsem_enter(int semid, int semno)
    Turn on the flag of semaphore then entering critical section.
    Definition: qsem.c:180
    diff --git a/doc/html/qshm_8c.html b/doc/html/qshm_8c.html index 6c46f94e..6c47a43d 100644 --- a/doc/html/qshm_8c.html +++ b/doc/html/qshm_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: ipc/qshm.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qshm.c File Reference
    +
    +
    qshm.c File Reference
    @@ -70,19 +70,19 @@

    Go to the source code of this file.

    - - + - + - - - + + + - +

    +

    Functions

    int qshm_init (const char *keyfile, int keyid, size_t size, bool recreate)
     Initialize shared-memory.
     Initialize shared-memory. More...
     
    int qshm_getid (const char *keyfile, int keyid)
     Get shared memory identifier by keyfile and keyid for existing shared memory.
     Get shared memory identifier by keyfile and keyid for existing shared memory. More...
     
    void * qshm_get (int shmid)
     Get a pointer of shared memory.
     
    void * qshm_get (int shmid)
     Get a pointer of shared memory. More...
     
    bool qshm_free (int shmid)
     De-allocate shared memory.
     De-allocate shared memory. More...
     

    Detailed Description

    @@ -94,14 +94,14 @@
    [shared memory creater]
    // create shared memory
    -
    int shmid = qshm_init("/some/file/for/generating/unique/key", 's', sizeof(struct SharedData), true);
    +
    int shmid = qshm_init("/some/file/for/generating/unique/key", 's', sizeof(struct SharedData), true);
    if(shmid < 0) {
    printf("ERROR: Can't initialize shared memory.\n");
    return -1;
    }
    // get shared memory pointer
    -
    struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    +
    struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    if(sdata == NULL) {
    printf("ERROR: Can't get shared memory.\n");
    return -1;
    @@ -109,27 +109,27 @@
    [shared memory user]
    // get shared memory id
    -
    int shmid = qshm_getid("/some/file/for/generating/unique/key", 's');
    +
    int shmid = qshm_getid("/some/file/for/generating/unique/key", 's');
    if(shmid < 0) {
    printf("ERROR: Can't get shared memory id.\n");
    return -1;
    }
    // get shared memory pointer
    -
    struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    +
    struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    if(sdata == NULL) {
    printf("ERROR: Can't get shared memory.\n");
    return -1;
    }
    -
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition qshm.c:149
    -
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition qshm.c:91
    -
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition qshm.c:127
    +
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition: qshm.c:149
    +
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition: qshm.c:91
    +
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition: qshm.c:127

    Definition in file qshm.c.

    Function Documentation

    - -

    ◆ qshm_init()

    + +

    ◆ qshm_init()

    @@ -182,8 +182,8 @@

    -

    ◆ qshm_getid()

    + +

    ◆ qshm_getid()

    @@ -222,14 +222,14 @@

    -

    ◆ qshm_get()

    + +

    ◆ qshm_get()

    - + @@ -251,8 +251,8 @@

    -

    ◆ qshm_free()

    + +

    ◆ qshm_free()

    @@ -286,7 +286,7 @@

      - +

    diff --git a/doc/html/qshm_8c.js b/doc/html/qshm_8c.js index 0dd08d85..adaebd5b 100644 --- a/doc/html/qshm_8c.js +++ b/doc/html/qshm_8c.js @@ -2,6 +2,6 @@ var qshm_8c = [ [ "qshm_init", "qshm_8c.html#a881b11a2d26ae83230151955545630ed", null ], [ "qshm_getid", "qshm_8c.html#aa6b1f31987b42fe18dc6be25e58c0bce", null ], - [ "qshm_get", "qshm_8c.html#a04d0a8cb2c672f568931132d0c1b5246", null ], + [ "qshm_get", "qshm_8c.html#a4ad7d06e37d47bb6c35a8c37b1c0a50e", null ], [ "qshm_free", "qshm_8c.html#aec816f3889e85322e0304c3cbd67c639", null ] ]; \ No newline at end of file diff --git a/doc/html/qshm_8c_source.html b/doc/html/qshm_8c_source.html index af2c6591..c5fed576 100644 --- a/doc/html/qshm_8c_source.html +++ b/doc/html/qshm_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: ipc/qshm.c Source File @@ -20,8 +20,8 @@

    void * qshm_get void* qshm_get ( int  shmid)
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,201 +52,202 @@

    -
    qshm.c
    +
    +
    qshm.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qshm.c Shared-memory APIs.
    -
    31 *
    -
    32 * @note
    -
    33 * @code
    -
    34 * [your header file]
    -
    35 * struct SharedData {
    -
    36 * (... structrue definitions ...)
    -
    37 * }
    -
    38 *
    -
    39 * [shared memory creater]
    -
    40 * // create shared memory
    -
    41 * int shmid = qshm_init("/some/file/for/generating/unique/key", 's', sizeof(struct SharedData), true);
    -
    42 * if(shmid < 0) {
    -
    43 * printf("ERROR: Can't initialize shared memory.\n");
    -
    44 * return -1;
    -
    45 * }
    -
    46 *
    -
    47 * // get shared memory pointer
    -
    48 * struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    -
    49 * if(sdata == NULL) {
    -
    50 * printf("ERROR: Can't get shared memory.\n");
    -
    51 * return -1;
    -
    52 * }
    -
    53 *
    -
    54 * [shared memory user]
    -
    55 * // get shared memory id
    -
    56 * int shmid = qshm_getid("/some/file/for/generating/unique/key", 's');
    -
    57 * if(shmid < 0) {
    -
    58 * printf("ERROR: Can't get shared memory id.\n");
    -
    59 * return -1;
    -
    60 * }
    -
    61 *
    -
    62 * // get shared memory pointer
    -
    63 * struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    -
    64 * if(sdata == NULL) {
    -
    65 * printf("ERROR: Can't get shared memory.\n");
    -
    66 * return -1;
    -
    67 * }
    -
    68 * @endcode
    -
    69 */
    -
    70
    -
    71#ifndef DISABLE_IPC
    -
    72
    -
    73#include <stdio.h>
    -
    74#include <stdlib.h>
    -
    75#include <stdbool.h>
    -
    76#include <string.h>
    -
    77#include <sys/shm.h>
    -
    78#include "qinternal.h"
    -
    79#include "ipc/qshm.h"
    -
    80
    -
    81/**
    -
    82 * Initialize shared-memory
    -
    83 *
    -
    84 * @param keyfile seed for generating unique IPC key
    -
    85 * @param keyid seed for generating unique IPC key
    -
    86 * @param size size of shared memory
    -
    87 * @param recreate set to true to re-create shared-memory if already exists
    -
    88 *
    -
    89 * @return non-negative shared memory identifier if successful, otherwise returns -1
    -
    90 */
    -
    91int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate) {
    -
    92 key_t semkey;
    -
    93 int shmid;
    -
    94
    -
    95 /* generate unique key using ftok() */
    -
    96 if (keyfile != NULL) {
    -
    97 semkey = ftok(keyfile, keyid);
    -
    98 if (semkey == -1)
    -
    99 return -1;
    -
    100 } else {
    -
    101 semkey = IPC_PRIVATE;
    -
    102 }
    -
    103
    -
    104 /* create shared memory */
    -
    105 if ((shmid = shmget(semkey, size, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
    -
    106 if (recreate == false)
    -
    107 return -1;
    -
    108
    -
    109 /* destroy & re-create */
    -
    110 if ((shmid = qshm_getid(keyfile, keyid)) >= 0)
    -
    111 qshm_free(shmid);
    -
    112 if ((shmid = shmget(semkey, size, IPC_CREAT | IPC_EXCL | 0666)) == -1)
    -
    113 return -1;
    -
    114 }
    -
    115
    -
    116 return shmid;
    -
    117}
    -
    118
    -
    119/**
    -
    120 * Get shared memory identifier by keyfile and keyid for existing shared memory
    -
    121 *
    -
    122 * @param keyfile seed for generating unique IPC key
    -
    123 * @param keyid seed for generating unique IPC key
    -
    124 *
    -
    125 * @return non-negative shared memory identifier if successful, otherwise returns -1
    -
    126 */
    -
    127int qshm_getid(const char *keyfile, int keyid) {
    -
    128 int shmid;
    -
    129
    -
    130 /* generate unique key using ftok() */
    -
    131 key_t semkey = ftok(keyfile, keyid);
    -
    132 if (semkey == -1)
    -
    133 return -1;
    -
    134
    -
    135 /* get current shared memory id */
    -
    136 if ((shmid = shmget(semkey, 0, 0)) == -1)
    -
    137 return -1;
    -
    138
    -
    139 return shmid;
    -
    140}
    -
    141
    -
    142/**
    -
    143 * Get a pointer of shared memory
    -
    144 *
    -
    145 * @param shmid shared memory identifier
    -
    146 *
    -
    147 * @return a pointer of shared memory
    -
    148 */
    -
    149void *qshm_get(int shmid) {
    -
    150 void *pShm;
    -
    151
    -
    152 if (shmid < 0)
    -
    153 return NULL;
    -
    154 pShm = shmat(shmid, 0, 0);
    -
    155 if (pShm == (void *) -1)
    -
    156 return NULL;
    -
    157 return pShm;
    -
    158}
    -
    159
    -
    160/**
    -
    161 * De-allocate shared memory
    -
    162 *
    -
    163 * @param shmid shared memory identifier
    -
    164 *
    -
    165 * @return true if successful, otherwise returns false
    -
    166 */
    -
    167bool qshm_free(int shmid) {
    -
    168 if (shmid < 0)
    -
    169 return false;
    -
    170 if (shmctl(shmid, IPC_RMID, 0) != 0)
    -
    171 return false;
    -
    172 return true;
    -
    173}
    -
    174
    -
    175#endif /* DISABLE_IPC */
    -
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition qshm.c:149
    -
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition qshm.c:91
    -
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition qshm.c:127
    -
    bool qshm_free(int shmid)
    De-allocate shared memory.
    Definition qshm.c:167
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qshm.c Shared-memory APIs.
    +
    31  *
    +
    32  * @note
    +
    33  * @code
    +
    34  * [your header file]
    +
    35  * struct SharedData {
    +
    36  * (... structrue definitions ...)
    +
    37  * }
    +
    38  *
    +
    39  * [shared memory creater]
    +
    40  * // create shared memory
    +
    41  * int shmid = qshm_init("/some/file/for/generating/unique/key", 's', sizeof(struct SharedData), true);
    +
    42  * if(shmid < 0) {
    +
    43  * printf("ERROR: Can't initialize shared memory.\n");
    +
    44  * return -1;
    +
    45  * }
    +
    46  *
    +
    47  * // get shared memory pointer
    +
    48  * struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    +
    49  * if(sdata == NULL) {
    +
    50  * printf("ERROR: Can't get shared memory.\n");
    +
    51  * return -1;
    +
    52  * }
    +
    53  *
    +
    54  * [shared memory user]
    +
    55  * // get shared memory id
    +
    56  * int shmid = qshm_getid("/some/file/for/generating/unique/key", 's');
    +
    57  * if(shmid < 0) {
    +
    58  * printf("ERROR: Can't get shared memory id.\n");
    +
    59  * return -1;
    +
    60  * }
    +
    61  *
    +
    62  * // get shared memory pointer
    +
    63  * struct SharedData *sdata = (SharedData *)qshm_get(shmid);
    +
    64  * if(sdata == NULL) {
    +
    65  * printf("ERROR: Can't get shared memory.\n");
    +
    66  * return -1;
    +
    67  * }
    +
    68  * @endcode
    +
    69  */
    +
    70 
    +
    71 #ifndef DISABLE_IPC
    +
    72 
    +
    73 #include <stdio.h>
    +
    74 #include <stdlib.h>
    +
    75 #include <stdbool.h>
    +
    76 #include <string.h>
    +
    77 #include <sys/shm.h>
    +
    78 #include "qinternal.h"
    +
    79 #include "ipc/qshm.h"
    +
    80 
    +
    81 /**
    +
    82  * Initialize shared-memory
    +
    83  *
    +
    84  * @param keyfile seed for generating unique IPC key
    +
    85  * @param keyid seed for generating unique IPC key
    +
    86  * @param size size of shared memory
    +
    87  * @param recreate set to true to re-create shared-memory if already exists
    +
    88  *
    +
    89  * @return non-negative shared memory identifier if successful, otherwise returns -1
    +
    90  */
    +
    91 int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate) {
    +
    92  key_t semkey;
    +
    93  int shmid;
    +
    94 
    +
    95  /* generate unique key using ftok() */
    +
    96  if (keyfile != NULL) {
    +
    97  semkey = ftok(keyfile, keyid);
    +
    98  if (semkey == -1)
    +
    99  return -1;
    +
    100  } else {
    +
    101  semkey = IPC_PRIVATE;
    +
    102  }
    +
    103 
    +
    104  /* create shared memory */
    +
    105  if ((shmid = shmget(semkey, size, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
    +
    106  if (recreate == false)
    +
    107  return -1;
    +
    108 
    +
    109  /* destroy & re-create */
    +
    110  if ((shmid = qshm_getid(keyfile, keyid)) >= 0)
    +
    111  qshm_free(shmid);
    +
    112  if ((shmid = shmget(semkey, size, IPC_CREAT | IPC_EXCL | 0666)) == -1)
    +
    113  return -1;
    +
    114  }
    +
    115 
    +
    116  return shmid;
    +
    117 }
    +
    118 
    +
    119 /**
    +
    120  * Get shared memory identifier by keyfile and keyid for existing shared memory
    +
    121  *
    +
    122  * @param keyfile seed for generating unique IPC key
    +
    123  * @param keyid seed for generating unique IPC key
    +
    124  *
    +
    125  * @return non-negative shared memory identifier if successful, otherwise returns -1
    +
    126  */
    +
    127 int qshm_getid(const char *keyfile, int keyid) {
    +
    128  int shmid;
    +
    129 
    +
    130  /* generate unique key using ftok() */
    +
    131  key_t semkey = ftok(keyfile, keyid);
    +
    132  if (semkey == -1)
    +
    133  return -1;
    +
    134 
    +
    135  /* get current shared memory id */
    +
    136  if ((shmid = shmget(semkey, 0, 0)) == -1)
    +
    137  return -1;
    +
    138 
    +
    139  return shmid;
    +
    140 }
    +
    141 
    +
    142 /**
    +
    143  * Get a pointer of shared memory
    +
    144  *
    +
    145  * @param shmid shared memory identifier
    +
    146  *
    +
    147  * @return a pointer of shared memory
    +
    148  */
    +
    149 void *qshm_get(int shmid) {
    +
    150  void *pShm;
    +
    151 
    +
    152  if (shmid < 0)
    +
    153  return NULL;
    +
    154  pShm = shmat(shmid, 0, 0);
    +
    155  if (pShm == (void *) -1)
    +
    156  return NULL;
    +
    157  return pShm;
    +
    158 }
    +
    159 
    +
    160 /**
    +
    161  * De-allocate shared memory
    +
    162  *
    +
    163  * @param shmid shared memory identifier
    +
    164  *
    +
    165  * @return true if successful, otherwise returns false
    +
    166  */
    +
    167 bool qshm_free(int shmid) {
    +
    168  if (shmid < 0)
    +
    169  return false;
    +
    170  if (shmctl(shmid, IPC_RMID, 0) != 0)
    +
    171  return false;
    +
    172  return true;
    +
    173 }
    +
    174 
    +
    175 #endif /* DISABLE_IPC */
    +
    void * qshm_get(int shmid)
    Get a pointer of shared memory.
    Definition: qshm.c:149
    +
    int qshm_init(const char *keyfile, int keyid, size_t size, bool recreate)
    Initialize shared-memory.
    Definition: qshm.c:91
    +
    int qshm_getid(const char *keyfile, int keyid)
    Get shared memory identifier by keyfile and keyid for existing shared memory.
    Definition: qshm.c:127
    +
    bool qshm_free(int shmid)
    De-allocate shared memory.
    Definition: qshm.c:167
    diff --git a/doc/html/qsocket_8c.html b/doc/html/qsocket_8c.html index b085da0d..7d435b3c 100644 --- a/doc/html/qsocket_8c.html +++ b/doc/html/qsocket_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qsocket.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qsocket.c File Reference
    +
    +
    qsocket.c File Reference
    @@ -70,28 +70,28 @@

    Go to the source code of this file.

    - - + - + - + - - - + + +

    +

    Functions

    int qsocket_open (const char *hostname, int port, int timeoutms)
     Create a TCP socket for the remote host and port.
     Create a TCP socket for the remote host and port. More...
     
    bool qsocket_close (int sockfd, int timeoutms)
     Close socket.
     Close socket. More...
     
    bool qsocket_get_addr (struct sockaddr_in *addr, const char *hostname, int port)
     Convert hostname to sockaddr_in structure.
     Convert hostname to sockaddr_in structure. More...
     
    char * qsocket_get_localaddr (char *buf, size_t bufsize)
     Return local IP address.
     
    char * qsocket_get_localaddr (char *buf, size_t bufsize)
     Return local IP address. More...
     

    Detailed Description

    Socket dandling APIs.

    Definition in file qsocket.c.

    Function Documentation

    - -

    ◆ qsocket_open()

    + +

    ◆ qsocket_open()

    @@ -137,8 +137,8 @@

    -

    ◆ qsocket_close()

    + +

    ◆ qsocket_close()

    @@ -177,8 +177,8 @@

    -

    ◆ qsocket_get_addr()

    + +

    ◆ qsocket_get_addr()

    @@ -224,14 +224,14 @@

    -

    ◆ qsocket_get_localaddr()

    + +

    ◆ qsocket_get_localaddr()

    - + @@ -263,7 +263,7 @@

    diff --git a/doc/html/qsocket_8c.js b/doc/html/qsocket_8c.js index dc80733c..1dc312a3 100644 --- a/doc/html/qsocket_8c.js +++ b/doc/html/qsocket_8c.js @@ -3,5 +3,5 @@ var qsocket_8c = [ "qsocket_open", "qsocket_8c.html#a86e043367b6ba401408c64736192319d", null ], [ "qsocket_close", "qsocket_8c.html#a536b1963c73ec5ace917a57ffed1f3cd", null ], [ "qsocket_get_addr", "qsocket_8c.html#a4285c6dd3c52560f3d471f332f6558bc", null ], - [ "qsocket_get_localaddr", "qsocket_8c.html#aefe8cc8f14190e19e81d9ec090007bd2", null ] + [ "qsocket_get_localaddr", "qsocket_8c.html#aac413225be88d873f0d6f19f2ee5e42b", null ] ]; \ No newline at end of file diff --git a/doc/html/qsocket_8c_source.html b/doc/html/qsocket_8c_source.html index feba9832..389480b1 100644 --- a/doc/html/qsocket_8c_source.html +++ b/doc/html/qsocket_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qsocket.c Source File @@ -20,8 +20,8 @@

    char * qsocket_get_localaddr char* qsocket_get_localaddr ( char *  buf,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,202 +52,203 @@

    -
    qsocket.c
    +
    +
    qsocket.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qsocket.c Socket dandling APIs.
    -
    31 */
    -
    32
    -
    33#ifndef _WIN32
    -
    34
    -
    35#include <stdio.h>
    -
    36#include <stdlib.h>
    -
    37#include <stdbool.h>
    -
    38#include <string.h>
    -
    39#include <stdarg.h>
    -
    40#include <netdb.h>
    -
    41#include <fcntl.h>
    -
    42#include <unistd.h>
    -
    43#include <errno.h>
    -
    44#include <arpa/inet.h>
    -
    45#include <netinet/in.h>
    -
    46#include <sys/socket.h>
    -
    47#include "qinternal.h"
    -
    48#include "utilities/qio.h"
    -
    49#include "utilities/qstring.h"
    -
    50#include "utilities/qsocket.h"
    -
    51
    -
    52/**
    -
    53 * Create a TCP socket for the remote host and port.
    -
    54 *
    -
    55 * @param hostname remote hostname
    -
    56 * @param port remote port
    -
    57 * @param timeoutms wait timeout milliseconds. if set to negative value,
    -
    58 * wait indefinitely.
    -
    59 *
    -
    60 * @return the new socket descriptor, or
    -
    61 * -1 in case of invalid hostname,
    -
    62 * -2 in case of socket creation failure,
    -
    63 * -3 in case of connection failure.
    -
    64 */
    -
    65int qsocket_open(const char *hostname, int port, int timeoutms) {
    -
    66 /* host conversion */
    -
    67 struct sockaddr_in addr;
    -
    68 if (qsocket_get_addr(&addr, hostname, port) == false) {
    -
    69 return -1; /* invalid hostname */
    -
    70 }
    -
    71
    -
    72 /* create new socket */
    -
    73 int sockfd;
    -
    74 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    -
    75 return -2; /* sockfd creation fail */
    -
    76 }
    -
    77
    -
    78 /* set to non-block socket*/
    -
    79 int flags = fcntl(sockfd, F_GETFL, 0);
    -
    80 if (timeoutms >= 0)
    -
    81 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    -
    82
    -
    83 /* try to connect */
    -
    84 int status = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
    -
    85 if (status < 0
    -
    86 && (errno != EINPROGRESS
    -
    87 || qio_wait_writable(sockfd, timeoutms) <= 0)) {
    -
    88 close(sockfd);
    -
    89 return -3; /* connection failed */
    -
    90 }
    -
    91
    -
    92 /* restore to block socket */
    -
    93 if (timeoutms >= 0)
    -
    94 fcntl(sockfd, F_SETFL, flags);
    -
    95
    -
    96 return sockfd;
    -
    97}
    -
    98
    -
    99/**
    -
    100 * Close socket.
    -
    101 *
    -
    102 * @param sockfd socket descriptor
    -
    103 * @param timeoutms if timeoutms >= 0, shut down write connection first then
    -
    104 * wait and throw out input stream data. set to -1 to close
    -
    105 * socket immediately.
    -
    106 *
    -
    107 * @return true on success, or false if an error occurred.
    -
    108 */
    -
    109bool qsocket_close(int sockfd, int timeoutms) {
    -
    110 // close connection
    -
    111 if (timeoutms >= 0 && shutdown(sockfd, SHUT_WR) == 0) {
    -
    112 char buf[1024];
    -
    113 while (true) {
    -
    114 ssize_t read = qio_read(sockfd, buf, sizeof(buf), timeoutms);
    -
    115 DEBUG("Throw %zu bytes from dummy input stream.", read);
    -
    116 if (read <= 0)
    -
    117 break;
    -
    118 }
    -
    119 }
    -
    120
    -
    121 if (close(sockfd) == 0)
    -
    122 return true;
    -
    123 return false;
    -
    124}
    -
    125
    -
    126/**
    -
    127 * Convert hostname to sockaddr_in structure.
    -
    128 *
    -
    129 * @param addr sockaddr_in structure pointer
    -
    130 * @param hostname IP string address or hostname
    -
    131 * @param port port number
    -
    132 *
    -
    133 * @return true if successful, otherwise returns false.
    -
    134 */
    -
    135bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port) {
    -
    136 /* here we assume that the hostname argument contains ip address */
    -
    137 memset((void *) addr, 0, sizeof(struct sockaddr_in));
    -
    138 if (!inet_aton(hostname, &addr->sin_addr)) { /* fail then try another way */
    -
    139 struct hostent *hp;
    -
    140 if ((hp = gethostbyname(hostname)) == 0)
    -
    141 return false;
    -
    142 memcpy(&addr->sin_addr, hp->h_addr, sizeof(struct in_addr));
    -
    143 }
    -
    144 addr->sin_family = AF_INET;
    -
    145 addr->sin_port = htons(port);
    -
    146
    -
    147 return true;
    -
    148}
    -
    149
    -
    150/**
    -
    151 * Return local IP address.
    -
    152 *
    -
    153 * @return malloced string pointer which contains IP address string if
    -
    154 * successful, otherwise returns NULL
    -
    155 */
    -
    156char *qsocket_get_localaddr(char *buf, size_t bufsize) {
    -
    157 char hostname[63 + 1];
    -
    158 if (gethostname(hostname, sizeof(hostname)) != 0)
    -
    159 return NULL;
    -
    160
    -
    161 struct hostent *hostentry = gethostbyname(hostname);
    -
    162 if (hostentry == NULL)
    -
    163 return NULL;
    -
    164
    -
    165 char *localip = inet_ntoa(*(struct in_addr *) *hostentry->h_addr_list);
    -
    166 if (localip == NULL)
    -
    167 return NULL;
    -
    168
    -
    169 qstrcpy(buf, bufsize, localip);
    -
    170 return buf;
    -
    171}
    -
    172
    -
    173#endif /* _WIN32 */
    -
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition qio.c:118
    -
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition qio.c:87
    -
    bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
    Convert hostname to sockaddr_in structure.
    Definition qsocket.c:135
    -
    bool qsocket_close(int sockfd, int timeoutms)
    Close socket.
    Definition qsocket.c:109
    -
    int qsocket_open(const char *hostname, int port, int timeoutms)
    Create a TCP socket for the remote host and port.
    Definition qsocket.c:65
    -
    char * qsocket_get_localaddr(char *buf, size_t bufsize)
    Return local IP address.
    Definition qsocket.c:156
    -
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition qstring.c:325
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qsocket.c Socket dandling APIs.
    +
    31  */
    +
    32 
    +
    33 #ifndef _WIN32
    +
    34 
    +
    35 #include <stdio.h>
    +
    36 #include <stdlib.h>
    +
    37 #include <stdbool.h>
    +
    38 #include <string.h>
    +
    39 #include <stdarg.h>
    +
    40 #include <netdb.h>
    +
    41 #include <fcntl.h>
    +
    42 #include <unistd.h>
    +
    43 #include <errno.h>
    +
    44 #include <arpa/inet.h>
    +
    45 #include <netinet/in.h>
    +
    46 #include <sys/socket.h>
    +
    47 #include "qinternal.h"
    +
    48 #include "utilities/qio.h"
    +
    49 #include "utilities/qstring.h"
    +
    50 #include "utilities/qsocket.h"
    +
    51 
    +
    52 /**
    +
    53  * Create a TCP socket for the remote host and port.
    +
    54  *
    +
    55  * @param hostname remote hostname
    +
    56  * @param port remote port
    +
    57  * @param timeoutms wait timeout milliseconds. if set to negative value,
    +
    58  * wait indefinitely.
    +
    59  *
    +
    60  * @return the new socket descriptor, or
    +
    61  * -1 in case of invalid hostname,
    +
    62  * -2 in case of socket creation failure,
    +
    63  * -3 in case of connection failure.
    +
    64  */
    +
    65 int qsocket_open(const char *hostname, int port, int timeoutms) {
    +
    66  /* host conversion */
    +
    67  struct sockaddr_in addr;
    +
    68  if (qsocket_get_addr(&addr, hostname, port) == false) {
    +
    69  return -1; /* invalid hostname */
    +
    70  }
    +
    71 
    +
    72  /* create new socket */
    +
    73  int sockfd;
    +
    74  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    +
    75  return -2; /* sockfd creation fail */
    +
    76  }
    +
    77 
    +
    78  /* set to non-block socket*/
    +
    79  int flags = fcntl(sockfd, F_GETFL, 0);
    +
    80  if (timeoutms >= 0)
    +
    81  fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
    +
    82 
    +
    83  /* try to connect */
    +
    84  int status = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
    +
    85  if (status < 0
    +
    86  && (errno != EINPROGRESS
    +
    87  || qio_wait_writable(sockfd, timeoutms) <= 0)) {
    +
    88  close(sockfd);
    +
    89  return -3; /* connection failed */
    +
    90  }
    +
    91 
    +
    92  /* restore to block socket */
    +
    93  if (timeoutms >= 0)
    +
    94  fcntl(sockfd, F_SETFL, flags);
    +
    95 
    +
    96  return sockfd;
    +
    97 }
    +
    98 
    +
    99 /**
    +
    100  * Close socket.
    +
    101  *
    +
    102  * @param sockfd socket descriptor
    +
    103  * @param timeoutms if timeoutms >= 0, shut down write connection first then
    +
    104  * wait and throw out input stream data. set to -1 to close
    +
    105  * socket immediately.
    +
    106  *
    +
    107  * @return true on success, or false if an error occurred.
    +
    108  */
    +
    109 bool qsocket_close(int sockfd, int timeoutms) {
    +
    110  // close connection
    +
    111  if (timeoutms >= 0 && shutdown(sockfd, SHUT_WR) == 0) {
    +
    112  char buf[1024];
    +
    113  while (true) {
    +
    114  ssize_t read = qio_read(sockfd, buf, sizeof(buf), timeoutms);
    +
    115  DEBUG("Throw %zu bytes from dummy input stream.", read);
    +
    116  if (read <= 0)
    +
    117  break;
    +
    118  }
    +
    119  }
    +
    120 
    +
    121  if (close(sockfd) == 0)
    +
    122  return true;
    +
    123  return false;
    +
    124 }
    +
    125 
    +
    126 /**
    +
    127  * Convert hostname to sockaddr_in structure.
    +
    128  *
    +
    129  * @param addr sockaddr_in structure pointer
    +
    130  * @param hostname IP string address or hostname
    +
    131  * @param port port number
    +
    132  *
    +
    133  * @return true if successful, otherwise returns false.
    +
    134  */
    +
    135 bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port) {
    +
    136  /* here we assume that the hostname argument contains ip address */
    +
    137  memset((void *) addr, 0, sizeof(struct sockaddr_in));
    +
    138  if (!inet_aton(hostname, &addr->sin_addr)) { /* fail then try another way */
    +
    139  struct hostent *hp;
    +
    140  if ((hp = gethostbyname(hostname)) == 0)
    +
    141  return false;
    +
    142  memcpy(&addr->sin_addr, hp->h_addr, sizeof(struct in_addr));
    +
    143  }
    +
    144  addr->sin_family = AF_INET;
    +
    145  addr->sin_port = htons(port);
    +
    146 
    +
    147  return true;
    +
    148 }
    +
    149 
    +
    150 /**
    +
    151  * Return local IP address.
    +
    152  *
    +
    153  * @return malloced string pointer which contains IP address string if
    +
    154  * successful, otherwise returns NULL
    +
    155  */
    +
    156 char *qsocket_get_localaddr(char *buf, size_t bufsize) {
    +
    157  char hostname[63 + 1];
    +
    158  if (gethostname(hostname, sizeof(hostname)) != 0)
    +
    159  return NULL;
    +
    160 
    +
    161  struct hostent *hostentry = gethostbyname(hostname);
    +
    162  if (hostentry == NULL)
    +
    163  return NULL;
    +
    164 
    +
    165  char *localip = inet_ntoa(*(struct in_addr *) *hostentry->h_addr_list);
    +
    166  if (localip == NULL)
    +
    167  return NULL;
    +
    168 
    +
    169  qstrcpy(buf, bufsize, localip);
    +
    170  return buf;
    +
    171 }
    +
    172 
    +
    173 #endif /* _WIN32 */
    +
    ssize_t qio_read(int fd, void *buf, size_t nbytes, int timeoutms)
    Read from a file descriptor.
    Definition: qio.c:118
    +
    int qio_wait_writable(int fd, int timeoutms)
    Test & wait until the file descriptor is ready for writing.
    Definition: qio.c:87
    +
    bool qsocket_get_addr(struct sockaddr_in *addr, const char *hostname, int port)
    Convert hostname to sockaddr_in structure.
    Definition: qsocket.c:135
    +
    bool qsocket_close(int sockfd, int timeoutms)
    Close socket.
    Definition: qsocket.c:109
    +
    int qsocket_open(const char *hostname, int port, int timeoutms)
    Create a TCP socket for the remote host and port.
    Definition: qsocket.c:65
    +
    char * qsocket_get_localaddr(char *buf, size_t bufsize)
    Return local IP address.
    Definition: qsocket.c:156
    +
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition: qstring.c:325
    diff --git a/doc/html/qstack_8c.html b/doc/html/qstack_8c.html index cc584f3e..5ded3a04 100644 --- a/doc/html/qstack_8c.html +++ b/doc/html/qstack_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qstack.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qstack.c File Reference
    +
    +
    qstack.c File Reference
    @@ -70,58 +70,58 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - - - - - - + + + + + + - + - - - - - - - - - + + + + + + + + + - + - - - + + + - + - + - + - +

    +

    Functions

    qstack_t * qstack (int options)
     Create a new stack container.
     
    qstack_t * qstack (int options)
     Create a new stack container. More...
     
    size_t qstack_setsize (qstack_t *stack, size_t max)
     qstack->setsize(): Sets maximum number of elements allowed in this stack.
     qstack->setsize(): Sets maximum number of elements allowed in this stack. More...
     
    bool qstack_push (qstack_t *stack, const void *data, size_t size)
     qstack->push(): Pushes an element onto the top of this stack.
     qstack->push(): Pushes an element onto the top of this stack. More...
     
    bool qstack_pushstr (qstack_t *stack, const char *str)
     qstack->pushstr(): Pushes a string onto the top of this stack.
     qstack->pushstr(): Pushes a string onto the top of this stack. More...
     
    bool qstack_pushint (qstack_t *stack, int64_t num)
     qstack->pushint(): Pushes a integer onto the top of this stack.
     qstack->pushint(): Pushes a integer onto the top of this stack. More...
     
    void * qstack_pop (qstack_t *stack, size_t *size)
     qstack->pop(): Removes a element at the top of this stack and returns that element.
     
    char * qstack_popstr (qstack_t *stack)
     qstack->popstr(): Removes a element at the top of this stack and returns that element.
     
    void * qstack_pop (qstack_t *stack, size_t *size)
     qstack->pop(): Removes a element at the top of this stack and returns that element. More...
     
    char * qstack_popstr (qstack_t *stack)
     qstack->popstr(): Removes a element at the top of this stack and returns that element. More...
     
    int64_t qstack_popint (qstack_t *stack)
     qstack->popint(): Removes a integer at the top of this stack and returns that element.
     qstack->popint(): Removes a integer at the top of this stack and returns that element. More...
     
    void * qstack_popat (qstack_t *stack, int index, size_t *size)
     qstack->popat(): Returns and remove the element at the specified position in this stack.
     
    void * qstack_get (qstack_t *stack, size_t *size, bool newmem)
     qstack->get(): Returns an element at the top of this stack without removing it.
     
    char * qstack_getstr (qstack_t *stack)
     qstack->getstr(): Returns an string at the top of this stack without removing it.
     
    void * qstack_popat (qstack_t *stack, int index, size_t *size)
     qstack->popat(): Returns and remove the element at the specified position in this stack. More...
     
    void * qstack_get (qstack_t *stack, size_t *size, bool newmem)
     qstack->get(): Returns an element at the top of this stack without removing it. More...
     
    char * qstack_getstr (qstack_t *stack)
     qstack->getstr(): Returns an string at the top of this stack without removing it. More...
     
    int64_t qstack_getint (qstack_t *stack)
     qstack->getint(): Returns an integer at the top of this stack without removing it.
     qstack->getint(): Returns an integer at the top of this stack without removing it. More...
     
    void * qstack_getat (qstack_t *stack, int index, size_t *size, bool newmem)
     qstack->getat(): Returns an element at the specified position in this stack without removing it.
     
    void * qstack_getat (qstack_t *stack, int index, size_t *size, bool newmem)
     qstack->getat(): Returns an element at the specified position in this stack without removing it. More...
     
    size_t qstack_size (qstack_t *stack)
     qstack->size(): Returns the number of elements in this stack.
     qstack->size(): Returns the number of elements in this stack. More...
     
    void qstack_clear (qstack_t *stack)
     qstack->clear(): Removes all of the elements from this stack.
     qstack->clear(): Removes all of the elements from this stack. More...
     
    bool qstack_debug (qstack_t *stack, FILE *out)
     qstack->debug(): Print out stored elements for debugging purpose.
     qstack->debug(): Print out stored elements for debugging purpose. More...
     
    void qstack_free (qstack_t *stack)
     qstack->free(): Free qstack_t
     qstack->free(): Free qstack_t More...
     

    Detailed Description

    @@ -134,7 +134,7 @@
    (positive index) 0 1 2
    (negative index) -3 -2 -1
    // create stack
    -
    qstack_t *stack = qstack(QSTACK_THREADSAFE);
    +
    qstack_t *stack = qstack(QSTACK_THREADSAFE);
    // example: integer stack
    stack->pushint(stack, 1);
    @@ -188,18 +188,18 @@
    pop(): C object
    pop(): B object
    pop(): A object
    -
    qstack_t * qstack(int options)
    Create a new stack container.
    Definition qstack.c:129
    +
    qstack_t * qstack(int options)
    Create a new stack container.
    Definition: qstack.c:129

    Definition in file qstack.c.

    Function Documentation

    - -

    ◆ qstack()

    + +

    ◆ qstack()

    - + @@ -225,7 +225,7 @@

    qstack_t *stack = qstack(QSTACK_THREADSAFE);
    +
    qstack_t *stack = qstack(QSTACK_THREADSAFE);
    Note
    Available options:
    • QSTACK_THREADSAFE - make it thread-safe.
    @@ -235,8 +235,8 @@

    -

    ◆ qstack_setsize()

    + +

    ◆ qstack_setsize()

    @@ -275,8 +275,8 @@

    -

    ◆ qstack_push()

    + +

    ◆ qstack_push()

    @@ -333,8 +333,8 @@

    -

    ◆ qstack_pushstr()

    + +

    ◆ qstack_pushstr()

    @@ -385,8 +385,8 @@

    -

    ◆ qstack_pushint()

    + +

    ◆ qstack_pushint()

    @@ -435,14 +435,14 @@

    -

    ◆ qstack_pop()

    + +

    ◆ qstack_pop()

    qstack_t * qstack qstack_t* qstack ( int  options)
    - + @@ -485,14 +485,14 @@

    -

    ◆ qstack_popstr()

    + +

    ◆ qstack_popstr()

    void * qstack_pop void* qstack_pop ( qstack_t *  stack,
    - + @@ -525,8 +525,8 @@

    -

    ◆ qstack_popint()

    + +

    ◆ qstack_popint()

    @@ -565,14 +565,14 @@

    -

    ◆ qstack_popat()

    + +

    ◆ qstack_popat()

    char * qstack_popstr char* qstack_popstr ( qstack_t *  stack)
    - + @@ -623,14 +623,14 @@

    -

    ◆ qstack_get()

    + +

    ◆ qstack_get()

    void * qstack_popat void* qstack_popat ( qstack_t *  stack,
    - + @@ -680,14 +680,14 @@

    -

    ◆ qstack_getstr()

    + +

    ◆ qstack_getstr()

    void * qstack_get void* qstack_get ( qstack_t *  stack,
    - + @@ -720,8 +720,8 @@

    -

    ◆ qstack_getint()

    + +

    ◆ qstack_getint()

    @@ -760,14 +760,14 @@

    -

    ◆ qstack_getat()

    + +

    ◆ qstack_getat()

    char * qstack_getstr char* qstack_getstr ( qstack_t *  stack)
    - + @@ -825,8 +825,8 @@

    -

    ◆ qstack_size()

    + +

    ◆ qstack_size()

    @@ -854,8 +854,8 @@

    -

    ◆ qstack_clear()

    + +

    ◆ qstack_clear()

    @@ -882,8 +882,8 @@

    -

    ◆ qstack_debug()

    + +

    ◆ qstack_debug()

    @@ -922,8 +922,8 @@

    -

    ◆ qstack_free()

    + +

    ◆ qstack_free()

    @@ -957,7 +957,7 @@

    diff --git a/doc/html/qstack_8c.js b/doc/html/qstack_8c.js index 115cac2d..b89d2167 100644 --- a/doc/html/qstack_8c.js +++ b/doc/html/qstack_8c.js @@ -1,18 +1,18 @@ var qstack_8c = [ - [ "qstack", "qstack_8c.html#a3ed81d0e8555fb6167d4be53c3ddb9ae", null ], + [ "qstack", "qstack_8c.html#ad59200fd9723a575e042365b916e150b", null ], [ "qstack_setsize", "qstack_8c.html#a243157bbd832b1a29da906cdf1e067d1", null ], [ "qstack_push", "qstack_8c.html#af6bd78d22c5ac2a5dbcc4102416a1682", null ], [ "qstack_pushstr", "qstack_8c.html#a3c626c12f0f8ad9b1899c4d6e9fa78a6", null ], [ "qstack_pushint", "qstack_8c.html#a227a2c394e088afee835b79aa1e81a90", null ], - [ "qstack_pop", "qstack_8c.html#a5f57044ab0d93ba510ad9ec44491d969", null ], - [ "qstack_popstr", "qstack_8c.html#ace3dc06378d4cde45bede6c4032a7151", null ], + [ "qstack_pop", "qstack_8c.html#a79ecf0838fc92a6fde5e43fcac757426", null ], + [ "qstack_popstr", "qstack_8c.html#a5a4924e9e1201c9049d0baa68d4c1da7", null ], [ "qstack_popint", "qstack_8c.html#a355b4b772da518e3e63702e61df44886", null ], - [ "qstack_popat", "qstack_8c.html#a1a467bd3711514b767b8093aa768ab1b", null ], - [ "qstack_get", "qstack_8c.html#a66b564e1ea27a75ec3fa4e13b9b34eb4", null ], - [ "qstack_getstr", "qstack_8c.html#a80484101311b715fbb867da8c3616c11", null ], + [ "qstack_popat", "qstack_8c.html#a20cfe4a51fa37348293d199b22530b04", null ], + [ "qstack_get", "qstack_8c.html#aae38a11a6be7fbea79f3ca51c65a4aee", null ], + [ "qstack_getstr", "qstack_8c.html#a6e2940f626e655f803cb78c452780c1a", null ], [ "qstack_getint", "qstack_8c.html#af37f84c47e9cd48b3f070fa96777029e", null ], - [ "qstack_getat", "qstack_8c.html#a9b0e45524a375b63cd1d07ee8b99665a", null ], + [ "qstack_getat", "qstack_8c.html#a513b235ab408d98c6324fbef841e3e1a", null ], [ "qstack_size", "qstack_8c.html#a3eac4be39a2ae68c7290735b034fc66c", null ], [ "qstack_clear", "qstack_8c.html#a1ca1db3402919fe147e32a16ee56a051", null ], [ "qstack_debug", "qstack_8c.html#af40fcb35c362981495c37aa7963ff50d", null ], diff --git a/doc/html/qstack_8c_source.html b/doc/html/qstack_8c_source.html index a4b8d5d8..c585e170 100644 --- a/doc/html/qstack_8c_source.html +++ b/doc/html/qstack_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qstack.c Source File @@ -20,8 +20,8 @@

    void * qstack_getat void* qstack_getat ( qstack_t *  stack,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,495 +52,496 @@
    -
    qstack.c
    +
    +
    qstack.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qstack.c Stack implementation.
    -
    31 *
    -
    32 * qstack container is a stack implementation. It represents a
    -
    33 * last-in-first-out(LIFO). It extends qlist container that allow a linked-list
    -
    34 * to be treated as a stack.
    -
    35 *
    -
    36 * @code
    -
    37 * [Conceptional Data Structure Diagram]
    -
    38 *
    -
    39 * top bottom
    -
    40 * DATA PUSH/POP <==> [ C ][ B ][ A ]
    -
    41 * (positive index) 0 1 2
    -
    42 * (negative index) -3 -2 -1
    -
    43 * @endcode
    -
    44 *
    -
    45 * @code
    -
    46 * // create stack
    -
    47 * qstack_t *stack = qstack(QSTACK_THREADSAFE);
    -
    48 *
    -
    49 * // example: integer stack
    -
    50 * stack->pushint(stack, 1);
    -
    51 * stack->pushint(stack, 2);
    -
    52 * stack->pushint(stack, 3);
    -
    53 *
    -
    54 * printf("popint(): %d\n", stack->popint(stack));
    -
    55 * printf("popint(): %d\n", stack->popint(stack));
    -
    56 * printf("popint(): %d\n", stack->popint(stack));
    -
    57 *
    -
    58 * // example: string stack
    -
    59 * stack->pushstr(stack, "A string");
    -
    60 * stack->pushstr(stack, "B string");
    -
    61 * stack->pushstr(stack, "C string");
    -
    62 *
    -
    63 * char *str = stack->popstr(stack);
    -
    64 * printf("popstr(): %s\n", str);
    -
    65 * free(str);
    -
    66 * str = stack->popstr(stack);
    -
    67 * printf("popstr(): %s\n", str);
    -
    68 * free(str);
    -
    69 * str = stack->popstr(stack);
    -
    70 * printf("popstr(): %s\n", str);
    -
    71 * free(str);
    -
    72 *
    -
    73 * // example: object stack
    -
    74 * stack->push(stack, "A object", sizeof("A object"));
    -
    75 * stack->push(stack, "B object", sizeof("B object"));
    -
    76 * stack->push(stack, "C object", sizeof("C object"));
    -
    77 *
    -
    78 * void *obj = stack->pop(stack, NULL);
    -
    79 * printf("pop(): %s\n", (char*)obj);
    -
    80 * free(obj);
    -
    81 * str = stack->pop(stack, NULL);
    -
    82 * printf("pop(): %s\n", (char*)obj);
    -
    83 * free(obj);
    -
    84 * str = stack->pop(stack, NULL);
    -
    85 * printf("pop(): %s\n", (char*)obj);
    -
    86 * free(obj);
    -
    87 *
    -
    88 * // release
    -
    89 * stack->free(stack);
    -
    90 *
    -
    91 * [Output]
    -
    92 * popint(): 3
    -
    93 * popint(): 2
    -
    94 * popint(): 1
    -
    95 * popstr(): C string
    -
    96 * popstr(): B string
    -
    97 * popstr(): A string
    -
    98 * pop(): C object
    -
    99 * pop(): B object
    -
    100 * pop(): A object
    -
    101 * @endcode
    -
    102 */
    -
    103
    -
    104#include <stdio.h>
    -
    105#include <stdlib.h>
    -
    106#include <stdbool.h>
    -
    107#include <string.h>
    -
    108#include <errno.h>
    -
    109#include "qinternal.h"
    -
    110#include "containers/qstack.h"
    -
    111
    -
    112/**
    -
    113 * Create a new stack container
    -
    114 *
    -
    115 * @param options combination of initialization options.
    -
    116 *
    -
    117 * @return a pointer of malloced qstack_t container, otherwise returns NULL.
    -
    118 * @retval errno will be set in error condition.
    -
    119 * - ENOMEM : Memory allocation failure.
    -
    120 *
    -
    121 * @code
    -
    122 * qstack_t *stack = qstack(QSTACK_THREADSAFE);
    -
    123 * @endcode
    -
    124 *
    -
    125 * @note
    -
    126 * Available options:
    -
    127 * - QSTACK_THREADSAFE - make it thread-safe.
    -
    128 */
    -
    129qstack_t *qstack(int options) {
    -
    130 qstack_t *stack = (qstack_t *) malloc(sizeof(qstack_t));
    -
    131 if (stack == NULL) {
    -
    132 errno = ENOMEM;
    -
    133 return NULL;
    -
    134 }
    -
    135
    -
    136 memset((void *) stack, 0, sizeof(qstack_t));
    -
    137 stack->list = qlist(options);
    -
    138 if (stack->list == NULL) {
    -
    139 free(stack);
    -
    140 return NULL;
    -
    141 }
    -
    142
    -
    143 // methods
    -
    144 stack->setsize = qstack_setsize;
    -
    145
    -
    146 stack->push = qstack_push;
    -
    147 stack->pushstr = qstack_pushstr;
    -
    148 stack->pushint = qstack_pushint;
    -
    149
    -
    150 stack->pop = qstack_pop;
    -
    151 stack->popstr = qstack_popstr;
    -
    152 stack->popint = qstack_popint;
    -
    153 stack->popat = qstack_popat;
    -
    154
    -
    155 stack->get = qstack_get;
    -
    156 stack->getstr = qstack_getstr;
    -
    157 stack->getint = qstack_getint;
    -
    158 stack->getat = qstack_getat;
    -
    159
    -
    160 stack->size = qstack_size;
    -
    161 stack->clear = qstack_clear;
    -
    162 stack->debug = qstack_debug;
    -
    163 stack->free = qstack_free;
    -
    164
    -
    165 return stack;
    -
    166}
    -
    167
    -
    168/**
    -
    169 * qstack->setsize(): Sets maximum number of elements allowed in this
    -
    170 * stack.
    -
    171 *
    -
    172 * @param stack qstack container pointer.
    -
    173 * @param max maximum number of elements. 0 means no limit.
    -
    174 *
    -
    175 * @return previous maximum number.
    -
    176 */
    -
    177size_t qstack_setsize(qstack_t *stack, size_t max) {
    -
    178 return stack->list->setsize(stack->list, max);
    -
    179}
    -
    180
    -
    181/**
    -
    182 * qstack->push(): Pushes an element onto the top of this stack.
    -
    183 *
    -
    184 * @param stack qstack container pointer.
    -
    185 * @param data a pointer which points data memory.
    -
    186 * @param size size of the data.
    -
    187 *
    -
    188 * @return true if successful, otherwise returns false.
    -
    189 * @retval errno will be set in error condition.
    -
    190 * - EINVAL : Invalid argument.
    -
    191 * - ENOBUFS : Stack full. Only happens when this stack has set to have
    -
    192 * limited number of elements)
    -
    193 * - ENOMEM : Memory allocation failure.
    -
    194 */
    -
    195bool qstack_push(qstack_t *stack, const void *data, size_t size) {
    -
    196 return stack->list->addfirst(stack->list, data, size);
    -
    197}
    -
    198
    -
    199/**
    -
    200 * qstack->pushstr(): Pushes a string onto the top of this stack.
    -
    201 *
    -
    202 * @param stack qstack container pointer.
    -
    203 * @param data a pointer which points data memory.
    -
    204 * @param size size of the data.
    -
    205 *
    -
    206 * @return true if successful, otherwise returns false.
    -
    207 * @retval errno will be set in error condition.
    -
    208 * - EINVAL : Invalid argument.
    -
    209 * - ENOBUFS : Stack full. Only happens when this stack has set to have
    -
    210 * limited number of elements.
    -
    211 * - ENOMEM : Memory allocation failure.
    -
    212 */
    -
    213bool qstack_pushstr(qstack_t *stack, const char *str) {
    -
    214 if (str == NULL) {
    -
    215 errno = EINVAL;
    -
    216 return false;
    -
    217 }
    -
    218 return stack->list->addfirst(stack->list, str, strlen(str) + 1);
    -
    219}
    -
    220
    -
    221/**
    -
    222 * qstack->pushint(): Pushes a integer onto the top of this stack.
    -
    223 *
    -
    224 * @param stack qstack container pointer.
    -
    225 * @param num integer data.
    -
    226 *
    -
    227 * @return true if successful, otherwise returns false.
    -
    228 * @retval errno will be set in error condition.
    -
    229 * - ENOBUFS : Stack full. Only happens when this stack has set to have
    -
    230 * limited number of elements.
    -
    231 * - ENOMEM : Memory allocation failure.
    -
    232 */
    -
    233bool qstack_pushint(qstack_t *stack, int64_t num) {
    -
    234 return stack->list->addfirst(stack->list, &num, sizeof(num));
    -
    235}
    -
    236
    -
    237/**
    -
    238 * qstack->pop(): Removes a element at the top of this stack and returns
    -
    239 * that element.
    -
    240 *
    -
    241 * @param stack qstack container pointer.
    -
    242 * @param size if size is not NULL, element size will be stored.
    -
    243 *
    -
    244 * @return a pointer of malloced element, otherwise returns NULL.
    -
    245 * @retval errno will be set in error condition.
    -
    246 * - ENOENT : Stack is empty.
    -
    247 * - ENOMEM : Memory allocation failure.
    -
    248 */
    -
    249void *qstack_pop(qstack_t *stack, size_t *size) {
    -
    250 return stack->list->popfirst(stack->list, size);
    -
    251}
    -
    252
    -
    253/**
    -
    254 * qstack->popstr(): Removes a element at the top of this stack and
    -
    255 * returns that element.
    -
    256 *
    -
    257 * @param stack qstack container pointer.
    -
    258 *
    -
    259 * @return a pointer of malloced string element, otherwise returns NULL.
    -
    260 * @retval errno will be set in error condition.
    -
    261 * - ENOENT : Stack is empty.
    -
    262 * - ENOMEM : Memory allocation failure.
    -
    263 *
    -
    264 * @note
    -
    265 * The string element should be pushed through pushstr().
    -
    266 */
    -
    267char *qstack_popstr(qstack_t *stack) {
    -
    268 size_t strsize;
    -
    269 char *str = stack->list->popfirst(stack->list, &strsize);
    -
    270 if (str != NULL) {
    -
    271 str[strsize - 1] = '\0'; // just to make sure
    -
    272 }
    -
    273
    -
    274 return str;
    -
    275}
    -
    276
    -
    277/**
    -
    278 * qstack->popint(): Removes a integer at the top of this stack and
    -
    279 * returns that element.
    -
    280 *
    -
    281 * @param stack qstack container pointer.
    -
    282 *
    -
    283 * @return an integer value, otherwise returns 0.
    -
    284 * @retval errno will be set in error condition.
    -
    285 * - ENOENT : Stack is empty.
    -
    286 * - ENOMEM : Memory allocation failure.
    -
    287 *
    -
    288 * @note
    -
    289 * The integer element should be pushed through pushint().
    -
    290 */
    -
    291int64_t qstack_popint(qstack_t *stack) {
    -
    292 int64_t num = 0;
    -
    293 int64_t *pnum = stack->list->popfirst(stack->list, NULL);
    -
    294 if (pnum != NULL) {
    -
    295 num = *pnum;
    -
    296 free(pnum);
    -
    297 }
    -
    298
    -
    299 return num;
    -
    300}
    -
    301
    -
    302/**
    -
    303 * qstack->popat(): Returns and remove the element at the specified
    -
    304 * position in this stack.
    -
    305 *
    -
    306 * @param stack qstack container pointer.
    -
    307 * @param index index at which the specified element is to be inserted
    -
    308 * @param size if size is not NULL, element size will be stored.
    -
    309 *
    -
    310 * @return a pointer of malloced element, otherwise returns NULL.
    -
    311 * @retval errno will be set in error condition.
    -
    312 * - ERANGE : Index out of range.
    -
    313 * - ENOMEM : Memory allocation failure.
    -
    314 *
    -
    315 * @note
    -
    316 * Negative index can be used for addressing a element from the bottom in
    -
    317 * this stack. For example, index -1 will always pop a element which is pushed
    -
    318 * at very first time.
    -
    319 */
    -
    320void *qstack_popat(qstack_t *stack, int index, size_t *size) {
    -
    321 return stack->list->popat(stack->list, index, size);
    -
    322}
    -
    323
    -
    324/**
    -
    325 * qstack->get(): Returns an element at the top of this stack without
    -
    326 * removing it.
    -
    327 *
    -
    328 * @param stack qstack container pointer.
    -
    329 * @param size if size is not NULL, element size will be stored.
    -
    330 * @param newmem whether or not to allocate memory for the element.
    -
    331 * @retval errno will be set in error condition.
    -
    332 * - ENOENT : Stack is empty.
    -
    333 * - ENOMEM : Memory allocation failure.
    -
    334 *
    -
    335 * @return a pointer of malloced element, otherwise returns NULL.
    -
    336 */
    -
    337void *qstack_get(qstack_t *stack, size_t *size, bool newmem) {
    -
    338 return stack->list->getfirst(stack->list, size, newmem);
    -
    339}
    -
    340
    -
    341/**
    -
    342 * qstack->getstr(): Returns an string at the top of this stack without
    -
    343 * removing it.
    -
    344 *
    -
    345 * @param stack qstack container pointer.
    -
    346 *
    -
    347 * @return a pointer of malloced string element, otherwise returns NULL.
    -
    348 * @retval errno will be set in error condition.
    -
    349 * - ENOENT : Stack is empty.
    -
    350 * - ENOMEM : Memory allocation failure.
    -
    351 *
    -
    352 * @note
    -
    353 * The string element should be pushed through pushstr().
    -
    354 */
    -
    355char *qstack_getstr(qstack_t *stack) {
    -
    356 size_t strsize;
    -
    357 char *str = stack->list->getfirst(stack->list, &strsize, true);
    -
    358 if (str != NULL) {
    -
    359 str[strsize - 1] = '\0'; // just to make sure
    -
    360 }
    -
    361
    -
    362 return str;
    -
    363}
    -
    364
    -
    365/**
    -
    366 * qstack->getint(): Returns an integer at the top of this stack without
    -
    367 * removing it.
    -
    368 *
    -
    369 * @param stack qstack container pointer.
    -
    370 *
    -
    371 * @return an integer value, otherwise returns 0.
    -
    372 * @retval errno will be set in error condition.
    -
    373 * - ENOENT : Stack is empty.
    -
    374 * - ENOMEM : Memory allocation failure.
    -
    375 *
    -
    376 * @note
    -
    377 * The integer element should be pushed through pushint().
    -
    378 */
    -
    379int64_t qstack_getint(qstack_t *stack) {
    -
    380 int64_t num = 0;
    -
    381 int64_t *pnum = stack->list->getfirst(stack->list, NULL, true);
    -
    382 if (pnum != NULL) {
    -
    383 num = *pnum;
    -
    384 free(pnum);
    -
    385 }
    -
    386
    -
    387 return num;
    -
    388}
    -
    389
    -
    390/**
    -
    391 * qstack->getat(): Returns an element at the specified position in this
    -
    392 * stack without removing it.
    -
    393 *
    -
    394 * @param stack qstack container pointer.
    -
    395 * @param index index at which the specified element is to be inserted
    -
    396 * @param size if size is not NULL, element size will be stored.
    -
    397 * @param newmem whether or not to allocate memory for the element.
    -
    398 *
    -
    399 * @return a pointer of element, otherwise returns NULL.
    -
    400 * @retval errno will be set in error condition.
    -
    401 * - ERANGE : Index out of range.
    -
    402 * - ENOMEM : Memory allocation failure.
    -
    403 *
    -
    404 * @note
    -
    405 * Negative index can be used for addressing a element from the bottom in this
    -
    406 * stack. For example, index -1 will always get a element which is pushed at
    -
    407 * very first time.
    -
    408 */
    -
    409void *qstack_getat(qstack_t *stack, int index, size_t *size, bool newmem) {
    -
    410 return stack->list->getat(stack->list, index, size, newmem);
    -
    411}
    -
    412
    -
    413/**
    -
    414 * qstack->size(): Returns the number of elements in this stack.
    -
    415 *
    -
    416 * @param stack qstack container pointer.
    -
    417 *
    -
    418 * @return the number of elements in this stack.
    -
    419 */
    -
    420size_t qstack_size(qstack_t *stack) {
    -
    421 return stack->list->size(stack->list);
    -
    422}
    -
    423
    -
    424/**
    -
    425 * qstack->clear(): Removes all of the elements from this stack.
    -
    426 *
    -
    427 * @param stack qstack container pointer.
    -
    428 */
    -
    429void qstack_clear(qstack_t *stack) {
    -
    430 stack->list->clear(stack->list);
    -
    431}
    -
    432
    -
    433/**
    -
    434 * qstack->debug(): Print out stored elements for debugging purpose.
    -
    435 *
    -
    436 * @param stack qstack container pointer.
    -
    437 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    438 *
    -
    439 * @return true if successful, otherwise returns false.
    -
    440 */
    -
    441bool qstack_debug(qstack_t *stack, FILE *out) {
    -
    442 return stack->list->debug(stack->list, out);
    -
    443}
    -
    444
    -
    445/**
    -
    446 * qstack->free(): Free qstack_t
    -
    447 *
    -
    448 * @param stack qstack container pointer.
    -
    449 *
    -
    450 * @return always returns true.
    -
    451 */
    -
    452void qstack_free(qstack_t *stack) {
    -
    453 stack->list->free(stack->list);
    -
    454 free(stack);
    -
    455}
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    -
    void * qstack_popat(qstack_t *stack, int index, size_t *size)
    qstack->popat(): Returns and remove the element at the specified position in this stack.
    Definition qstack.c:320
    -
    void qstack_clear(qstack_t *stack)
    qstack->clear(): Removes all of the elements from this stack.
    Definition qstack.c:429
    -
    bool qstack_pushint(qstack_t *stack, int64_t num)
    qstack->pushint(): Pushes a integer onto the top of this stack.
    Definition qstack.c:233
    -
    size_t qstack_setsize(qstack_t *stack, size_t max)
    qstack->setsize(): Sets maximum number of elements allowed in this stack.
    Definition qstack.c:177
    -
    void qstack_free(qstack_t *stack)
    qstack->free(): Free qstack_t
    Definition qstack.c:452
    -
    int64_t qstack_popint(qstack_t *stack)
    qstack->popint(): Removes a integer at the top of this stack and returns that element.
    Definition qstack.c:291
    -
    bool qstack_pushstr(qstack_t *stack, const char *str)
    qstack->pushstr(): Pushes a string onto the top of this stack.
    Definition qstack.c:213
    -
    size_t qstack_size(qstack_t *stack)
    qstack->size(): Returns the number of elements in this stack.
    Definition qstack.c:420
    -
    qstack_t * qstack(int options)
    Create a new stack container.
    Definition qstack.c:129
    -
    void * qstack_pop(qstack_t *stack, size_t *size)
    qstack->pop(): Removes a element at the top of this stack and returns that element.
    Definition qstack.c:249
    -
    void * qstack_get(qstack_t *stack, size_t *size, bool newmem)
    qstack->get(): Returns an element at the top of this stack without removing it.
    Definition qstack.c:337
    -
    char * qstack_getstr(qstack_t *stack)
    qstack->getstr(): Returns an string at the top of this stack without removing it.
    Definition qstack.c:355
    -
    void * qstack_getat(qstack_t *stack, int index, size_t *size, bool newmem)
    qstack->getat(): Returns an element at the specified position in this stack without removing it.
    Definition qstack.c:409
    -
    char * qstack_popstr(qstack_t *stack)
    qstack->popstr(): Removes a element at the top of this stack and returns that element.
    Definition qstack.c:267
    -
    int64_t qstack_getint(qstack_t *stack)
    qstack->getint(): Returns an integer at the top of this stack without removing it.
    Definition qstack.c:379
    -
    bool qstack_debug(qstack_t *stack, FILE *out)
    qstack->debug(): Print out stored elements for debugging purpose.
    Definition qstack.c:441
    -
    bool qstack_push(qstack_t *stack, const void *data, size_t size)
    qstack->push(): Pushes an element onto the top of this stack.
    Definition qstack.c:195
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qstack.c Stack implementation.
    +
    31  *
    +
    32  * qstack container is a stack implementation. It represents a
    +
    33  * last-in-first-out(LIFO). It extends qlist container that allow a linked-list
    +
    34  * to be treated as a stack.
    +
    35  *
    +
    36  * @code
    +
    37  * [Conceptional Data Structure Diagram]
    +
    38  *
    +
    39  * top bottom
    +
    40  * DATA PUSH/POP <==> [ C ][ B ][ A ]
    +
    41  * (positive index) 0 1 2
    +
    42  * (negative index) -3 -2 -1
    +
    43  * @endcode
    +
    44  *
    +
    45  * @code
    +
    46  * // create stack
    +
    47  * qstack_t *stack = qstack(QSTACK_THREADSAFE);
    +
    48  *
    +
    49  * // example: integer stack
    +
    50  * stack->pushint(stack, 1);
    +
    51  * stack->pushint(stack, 2);
    +
    52  * stack->pushint(stack, 3);
    +
    53  *
    +
    54  * printf("popint(): %d\n", stack->popint(stack));
    +
    55  * printf("popint(): %d\n", stack->popint(stack));
    +
    56  * printf("popint(): %d\n", stack->popint(stack));
    +
    57  *
    +
    58  * // example: string stack
    +
    59  * stack->pushstr(stack, "A string");
    +
    60  * stack->pushstr(stack, "B string");
    +
    61  * stack->pushstr(stack, "C string");
    +
    62  *
    +
    63  * char *str = stack->popstr(stack);
    +
    64  * printf("popstr(): %s\n", str);
    +
    65  * free(str);
    +
    66  * str = stack->popstr(stack);
    +
    67  * printf("popstr(): %s\n", str);
    +
    68  * free(str);
    +
    69  * str = stack->popstr(stack);
    +
    70  * printf("popstr(): %s\n", str);
    +
    71  * free(str);
    +
    72  *
    +
    73  * // example: object stack
    +
    74  * stack->push(stack, "A object", sizeof("A object"));
    +
    75  * stack->push(stack, "B object", sizeof("B object"));
    +
    76  * stack->push(stack, "C object", sizeof("C object"));
    +
    77  *
    +
    78  * void *obj = stack->pop(stack, NULL);
    +
    79  * printf("pop(): %s\n", (char*)obj);
    +
    80  * free(obj);
    +
    81  * str = stack->pop(stack, NULL);
    +
    82  * printf("pop(): %s\n", (char*)obj);
    +
    83  * free(obj);
    +
    84  * str = stack->pop(stack, NULL);
    +
    85  * printf("pop(): %s\n", (char*)obj);
    +
    86  * free(obj);
    +
    87  *
    +
    88  * // release
    +
    89  * stack->free(stack);
    +
    90  *
    +
    91  * [Output]
    +
    92  * popint(): 3
    +
    93  * popint(): 2
    +
    94  * popint(): 1
    +
    95  * popstr(): C string
    +
    96  * popstr(): B string
    +
    97  * popstr(): A string
    +
    98  * pop(): C object
    +
    99  * pop(): B object
    +
    100  * pop(): A object
    +
    101  * @endcode
    +
    102  */
    +
    103 
    +
    104 #include <stdio.h>
    +
    105 #include <stdlib.h>
    +
    106 #include <stdbool.h>
    +
    107 #include <string.h>
    +
    108 #include <errno.h>
    +
    109 #include "qinternal.h"
    +
    110 #include "containers/qstack.h"
    +
    111 
    +
    112 /**
    +
    113  * Create a new stack container
    +
    114  *
    +
    115  * @param options combination of initialization options.
    +
    116  *
    +
    117  * @return a pointer of malloced qstack_t container, otherwise returns NULL.
    +
    118  * @retval errno will be set in error condition.
    +
    119  * - ENOMEM : Memory allocation failure.
    +
    120  *
    +
    121  * @code
    +
    122  * qstack_t *stack = qstack(QSTACK_THREADSAFE);
    +
    123  * @endcode
    +
    124  *
    +
    125  * @note
    +
    126  * Available options:
    +
    127  * - QSTACK_THREADSAFE - make it thread-safe.
    +
    128  */
    +
    129 qstack_t *qstack(int options) {
    +
    130  qstack_t *stack = (qstack_t *) malloc(sizeof(qstack_t));
    +
    131  if (stack == NULL) {
    +
    132  errno = ENOMEM;
    +
    133  return NULL;
    +
    134  }
    +
    135 
    +
    136  memset((void *) stack, 0, sizeof(qstack_t));
    +
    137  stack->list = qlist(options);
    +
    138  if (stack->list == NULL) {
    +
    139  free(stack);
    +
    140  return NULL;
    +
    141  }
    +
    142 
    +
    143  // methods
    +
    144  stack->setsize = qstack_setsize;
    +
    145 
    +
    146  stack->push = qstack_push;
    +
    147  stack->pushstr = qstack_pushstr;
    +
    148  stack->pushint = qstack_pushint;
    +
    149 
    +
    150  stack->pop = qstack_pop;
    +
    151  stack->popstr = qstack_popstr;
    +
    152  stack->popint = qstack_popint;
    +
    153  stack->popat = qstack_popat;
    +
    154 
    +
    155  stack->get = qstack_get;
    +
    156  stack->getstr = qstack_getstr;
    +
    157  stack->getint = qstack_getint;
    +
    158  stack->getat = qstack_getat;
    +
    159 
    +
    160  stack->size = qstack_size;
    +
    161  stack->clear = qstack_clear;
    +
    162  stack->debug = qstack_debug;
    +
    163  stack->free = qstack_free;
    +
    164 
    +
    165  return stack;
    +
    166 }
    +
    167 
    +
    168 /**
    +
    169  * qstack->setsize(): Sets maximum number of elements allowed in this
    +
    170  * stack.
    +
    171  *
    +
    172  * @param stack qstack container pointer.
    +
    173  * @param max maximum number of elements. 0 means no limit.
    +
    174  *
    +
    175  * @return previous maximum number.
    +
    176  */
    +
    177 size_t qstack_setsize(qstack_t *stack, size_t max) {
    +
    178  return stack->list->setsize(stack->list, max);
    +
    179 }
    +
    180 
    +
    181 /**
    +
    182  * qstack->push(): Pushes an element onto the top of this stack.
    +
    183  *
    +
    184  * @param stack qstack container pointer.
    +
    185  * @param data a pointer which points data memory.
    +
    186  * @param size size of the data.
    +
    187  *
    +
    188  * @return true if successful, otherwise returns false.
    +
    189  * @retval errno will be set in error condition.
    +
    190  * - EINVAL : Invalid argument.
    +
    191  * - ENOBUFS : Stack full. Only happens when this stack has set to have
    +
    192  * limited number of elements)
    +
    193  * - ENOMEM : Memory allocation failure.
    +
    194  */
    +
    195 bool qstack_push(qstack_t *stack, const void *data, size_t size) {
    +
    196  return stack->list->addfirst(stack->list, data, size);
    +
    197 }
    +
    198 
    +
    199 /**
    +
    200  * qstack->pushstr(): Pushes a string onto the top of this stack.
    +
    201  *
    +
    202  * @param stack qstack container pointer.
    +
    203  * @param data a pointer which points data memory.
    +
    204  * @param size size of the data.
    +
    205  *
    +
    206  * @return true if successful, otherwise returns false.
    +
    207  * @retval errno will be set in error condition.
    +
    208  * - EINVAL : Invalid argument.
    +
    209  * - ENOBUFS : Stack full. Only happens when this stack has set to have
    +
    210  * limited number of elements.
    +
    211  * - ENOMEM : Memory allocation failure.
    +
    212  */
    +
    213 bool qstack_pushstr(qstack_t *stack, const char *str) {
    +
    214  if (str == NULL) {
    +
    215  errno = EINVAL;
    +
    216  return false;
    +
    217  }
    +
    218  return stack->list->addfirst(stack->list, str, strlen(str) + 1);
    +
    219 }
    +
    220 
    +
    221 /**
    +
    222  * qstack->pushint(): Pushes a integer onto the top of this stack.
    +
    223  *
    +
    224  * @param stack qstack container pointer.
    +
    225  * @param num integer data.
    +
    226  *
    +
    227  * @return true if successful, otherwise returns false.
    +
    228  * @retval errno will be set in error condition.
    +
    229  * - ENOBUFS : Stack full. Only happens when this stack has set to have
    +
    230  * limited number of elements.
    +
    231  * - ENOMEM : Memory allocation failure.
    +
    232  */
    +
    233 bool qstack_pushint(qstack_t *stack, int64_t num) {
    +
    234  return stack->list->addfirst(stack->list, &num, sizeof(num));
    +
    235 }
    +
    236 
    +
    237 /**
    +
    238  * qstack->pop(): Removes a element at the top of this stack and returns
    +
    239  * that element.
    +
    240  *
    +
    241  * @param stack qstack container pointer.
    +
    242  * @param size if size is not NULL, element size will be stored.
    +
    243  *
    +
    244  * @return a pointer of malloced element, otherwise returns NULL.
    +
    245  * @retval errno will be set in error condition.
    +
    246  * - ENOENT : Stack is empty.
    +
    247  * - ENOMEM : Memory allocation failure.
    +
    248  */
    +
    249 void *qstack_pop(qstack_t *stack, size_t *size) {
    +
    250  return stack->list->popfirst(stack->list, size);
    +
    251 }
    +
    252 
    +
    253 /**
    +
    254  * qstack->popstr(): Removes a element at the top of this stack and
    +
    255  * returns that element.
    +
    256  *
    +
    257  * @param stack qstack container pointer.
    +
    258  *
    +
    259  * @return a pointer of malloced string element, otherwise returns NULL.
    +
    260  * @retval errno will be set in error condition.
    +
    261  * - ENOENT : Stack is empty.
    +
    262  * - ENOMEM : Memory allocation failure.
    +
    263  *
    +
    264  * @note
    +
    265  * The string element should be pushed through pushstr().
    +
    266  */
    +
    267 char *qstack_popstr(qstack_t *stack) {
    +
    268  size_t strsize;
    +
    269  char *str = stack->list->popfirst(stack->list, &strsize);
    +
    270  if (str != NULL) {
    +
    271  str[strsize - 1] = '\0'; // just to make sure
    +
    272  }
    +
    273 
    +
    274  return str;
    +
    275 }
    +
    276 
    +
    277 /**
    +
    278  * qstack->popint(): Removes a integer at the top of this stack and
    +
    279  * returns that element.
    +
    280  *
    +
    281  * @param stack qstack container pointer.
    +
    282  *
    +
    283  * @return an integer value, otherwise returns 0.
    +
    284  * @retval errno will be set in error condition.
    +
    285  * - ENOENT : Stack is empty.
    +
    286  * - ENOMEM : Memory allocation failure.
    +
    287  *
    +
    288  * @note
    +
    289  * The integer element should be pushed through pushint().
    +
    290  */
    +
    291 int64_t qstack_popint(qstack_t *stack) {
    +
    292  int64_t num = 0;
    +
    293  int64_t *pnum = stack->list->popfirst(stack->list, NULL);
    +
    294  if (pnum != NULL) {
    +
    295  num = *pnum;
    +
    296  free(pnum);
    +
    297  }
    +
    298 
    +
    299  return num;
    +
    300 }
    +
    301 
    +
    302 /**
    +
    303  * qstack->popat(): Returns and remove the element at the specified
    +
    304  * position in this stack.
    +
    305  *
    +
    306  * @param stack qstack container pointer.
    +
    307  * @param index index at which the specified element is to be inserted
    +
    308  * @param size if size is not NULL, element size will be stored.
    +
    309  *
    +
    310  * @return a pointer of malloced element, otherwise returns NULL.
    +
    311  * @retval errno will be set in error condition.
    +
    312  * - ERANGE : Index out of range.
    +
    313  * - ENOMEM : Memory allocation failure.
    +
    314  *
    +
    315  * @note
    +
    316  * Negative index can be used for addressing a element from the bottom in
    +
    317  * this stack. For example, index -1 will always pop a element which is pushed
    +
    318  * at very first time.
    +
    319  */
    +
    320 void *qstack_popat(qstack_t *stack, int index, size_t *size) {
    +
    321  return stack->list->popat(stack->list, index, size);
    +
    322 }
    +
    323 
    +
    324 /**
    +
    325  * qstack->get(): Returns an element at the top of this stack without
    +
    326  * removing it.
    +
    327  *
    +
    328  * @param stack qstack container pointer.
    +
    329  * @param size if size is not NULL, element size will be stored.
    +
    330  * @param newmem whether or not to allocate memory for the element.
    +
    331  * @retval errno will be set in error condition.
    +
    332  * - ENOENT : Stack is empty.
    +
    333  * - ENOMEM : Memory allocation failure.
    +
    334  *
    +
    335  * @return a pointer of malloced element, otherwise returns NULL.
    +
    336  */
    +
    337 void *qstack_get(qstack_t *stack, size_t *size, bool newmem) {
    +
    338  return stack->list->getfirst(stack->list, size, newmem);
    +
    339 }
    +
    340 
    +
    341 /**
    +
    342  * qstack->getstr(): Returns an string at the top of this stack without
    +
    343  * removing it.
    +
    344  *
    +
    345  * @param stack qstack container pointer.
    +
    346  *
    +
    347  * @return a pointer of malloced string element, otherwise returns NULL.
    +
    348  * @retval errno will be set in error condition.
    +
    349  * - ENOENT : Stack is empty.
    +
    350  * - ENOMEM : Memory allocation failure.
    +
    351  *
    +
    352  * @note
    +
    353  * The string element should be pushed through pushstr().
    +
    354  */
    +
    355 char *qstack_getstr(qstack_t *stack) {
    +
    356  size_t strsize;
    +
    357  char *str = stack->list->getfirst(stack->list, &strsize, true);
    +
    358  if (str != NULL) {
    +
    359  str[strsize - 1] = '\0'; // just to make sure
    +
    360  }
    +
    361 
    +
    362  return str;
    +
    363 }
    +
    364 
    +
    365 /**
    +
    366  * qstack->getint(): Returns an integer at the top of this stack without
    +
    367  * removing it.
    +
    368  *
    +
    369  * @param stack qstack container pointer.
    +
    370  *
    +
    371  * @return an integer value, otherwise returns 0.
    +
    372  * @retval errno will be set in error condition.
    +
    373  * - ENOENT : Stack is empty.
    +
    374  * - ENOMEM : Memory allocation failure.
    +
    375  *
    +
    376  * @note
    +
    377  * The integer element should be pushed through pushint().
    +
    378  */
    +
    379 int64_t qstack_getint(qstack_t *stack) {
    +
    380  int64_t num = 0;
    +
    381  int64_t *pnum = stack->list->getfirst(stack->list, NULL, true);
    +
    382  if (pnum != NULL) {
    +
    383  num = *pnum;
    +
    384  free(pnum);
    +
    385  }
    +
    386 
    +
    387  return num;
    +
    388 }
    +
    389 
    +
    390 /**
    +
    391  * qstack->getat(): Returns an element at the specified position in this
    +
    392  * stack without removing it.
    +
    393  *
    +
    394  * @param stack qstack container pointer.
    +
    395  * @param index index at which the specified element is to be inserted
    +
    396  * @param size if size is not NULL, element size will be stored.
    +
    397  * @param newmem whether or not to allocate memory for the element.
    +
    398  *
    +
    399  * @return a pointer of element, otherwise returns NULL.
    +
    400  * @retval errno will be set in error condition.
    +
    401  * - ERANGE : Index out of range.
    +
    402  * - ENOMEM : Memory allocation failure.
    +
    403  *
    +
    404  * @note
    +
    405  * Negative index can be used for addressing a element from the bottom in this
    +
    406  * stack. For example, index -1 will always get a element which is pushed at
    +
    407  * very first time.
    +
    408  */
    +
    409 void *qstack_getat(qstack_t *stack, int index, size_t *size, bool newmem) {
    +
    410  return stack->list->getat(stack->list, index, size, newmem);
    +
    411 }
    +
    412 
    +
    413 /**
    +
    414  * qstack->size(): Returns the number of elements in this stack.
    +
    415  *
    +
    416  * @param stack qstack container pointer.
    +
    417  *
    +
    418  * @return the number of elements in this stack.
    +
    419  */
    +
    420 size_t qstack_size(qstack_t *stack) {
    +
    421  return stack->list->size(stack->list);
    +
    422 }
    +
    423 
    +
    424 /**
    +
    425  * qstack->clear(): Removes all of the elements from this stack.
    +
    426  *
    +
    427  * @param stack qstack container pointer.
    +
    428  */
    +
    429 void qstack_clear(qstack_t *stack) {
    +
    430  stack->list->clear(stack->list);
    +
    431 }
    +
    432 
    +
    433 /**
    +
    434  * qstack->debug(): Print out stored elements for debugging purpose.
    +
    435  *
    +
    436  * @param stack qstack container pointer.
    +
    437  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    438  *
    +
    439  * @return true if successful, otherwise returns false.
    +
    440  */
    +
    441 bool qstack_debug(qstack_t *stack, FILE *out) {
    +
    442  return stack->list->debug(stack->list, out);
    +
    443 }
    +
    444 
    +
    445 /**
    +
    446  * qstack->free(): Free qstack_t
    +
    447  *
    +
    448  * @param stack qstack container pointer.
    +
    449  *
    +
    450  * @return always returns true.
    +
    451  */
    +
    452 void qstack_free(qstack_t *stack) {
    +
    453  stack->list->free(stack->list);
    +
    454  free(stack);
    +
    455 }
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124
    +
    void qstack_clear(qstack_t *stack)
    qstack->clear(): Removes all of the elements from this stack.
    Definition: qstack.c:429
    +
    void * qstack_popat(qstack_t *stack, int index, size_t *size)
    qstack->popat(): Returns and remove the element at the specified position in this stack.
    Definition: qstack.c:320
    +
    bool qstack_pushint(qstack_t *stack, int64_t num)
    qstack->pushint(): Pushes a integer onto the top of this stack.
    Definition: qstack.c:233
    +
    size_t qstack_setsize(qstack_t *stack, size_t max)
    qstack->setsize(): Sets maximum number of elements allowed in this stack.
    Definition: qstack.c:177
    +
    void qstack_free(qstack_t *stack)
    qstack->free(): Free qstack_t
    Definition: qstack.c:452
    +
    int64_t qstack_popint(qstack_t *stack)
    qstack->popint(): Removes a integer at the top of this stack and returns that element.
    Definition: qstack.c:291
    +
    bool qstack_pushstr(qstack_t *stack, const char *str)
    qstack->pushstr(): Pushes a string onto the top of this stack.
    Definition: qstack.c:213
    +
    size_t qstack_size(qstack_t *stack)
    qstack->size(): Returns the number of elements in this stack.
    Definition: qstack.c:420
    +
    void * qstack_getat(qstack_t *stack, int index, size_t *size, bool newmem)
    qstack->getat(): Returns an element at the specified position in this stack without removing it.
    Definition: qstack.c:409
    +
    char * qstack_popstr(qstack_t *stack)
    qstack->popstr(): Removes a element at the top of this stack and returns that element.
    Definition: qstack.c:267
    +
    char * qstack_getstr(qstack_t *stack)
    qstack->getstr(): Returns an string at the top of this stack without removing it.
    Definition: qstack.c:355
    +
    void * qstack_pop(qstack_t *stack, size_t *size)
    qstack->pop(): Removes a element at the top of this stack and returns that element.
    Definition: qstack.c:249
    +
    void * qstack_get(qstack_t *stack, size_t *size, bool newmem)
    qstack->get(): Returns an element at the top of this stack without removing it.
    Definition: qstack.c:337
    +
    qstack_t * qstack(int options)
    Create a new stack container.
    Definition: qstack.c:129
    +
    int64_t qstack_getint(qstack_t *stack)
    qstack->getint(): Returns an integer at the top of this stack without removing it.
    Definition: qstack.c:379
    +
    bool qstack_debug(qstack_t *stack, FILE *out)
    qstack->debug(): Print out stored elements for debugging purpose.
    Definition: qstack.c:441
    +
    bool qstack_push(qstack_t *stack, const void *data, size_t size)
    qstack->push(): Pushes an element onto the top of this stack.
    Definition: qstack.c:195
    diff --git a/doc/html/qstring_8c.html b/doc/html/qstring_8c.html index 8469c954..9947313f 100644 --- a/doc/html/qstring_8c.html +++ b/doc/html/qstring_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qstring.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@

    @@ -61,7 +60,8 @@
    -
    qstring.c File Reference
    +
    +
    qstring.c File Reference
    @@ -70,91 +70,91 @@

    Go to the source code of this file.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - - - + + +

    +

    Functions

    char * qstrtrim (char *str)
     Remove white spaces(including CR, LF) from head and tail of the string.
     
    char * qstrtrim_head (char *str)
     Remove heading white spaces of the string.
     
    char * qstrtrim_tail (char *str)
     Remove tailing white spaces(including CR, LF) of the string.
     
    char * qstrunchar (char *str, char head, char tail)
     Remove character from head and tail of the string.
     
    char * qstrreplace (const char *mode, char *srcstr, const char *tokstr, const char *word)
     Replace string or tokens as word from source string with given mode.
     
    char * qstrcpy (char *dst, size_t size, const char *src)
     Copy src string to dst.
     
    char * qstrncpy (char *dst, size_t size, const char *src, size_t nbytes)
     Copy src string to dst no more than n bytes.
     
    char * qstrdupf (const char *format,...)
     Duplicate a formatted string.
     
    char * qstrdup_between (const char *str, const char *start, const char *end)
     Duplicate a substing set.
     
    void * qmemdup (const void *data, size_t size)
     Duplicate a copy of memory data.
     
    char * qstrcatf (char *str, const char *format,...)
     Append formatted string to the end of the source str.
     
    char * qstrgets (char *buf, size_t size, char **offset)
     Get one line from the string offset.
     
    char * qstrrev (char *str)
     Reverse the order of characters in the string.
     
    char * qstrupper (char *str)
     Convert character to bigger character.
     
    char * qstrlower (char *str)
     Convert character to lower character.
     
    char * qstrtok (char *str, const char *delimiters, char *retstop, int *offset)
     Split string into tokens.
     
    qlist_t * qstrtokenizer (const char *str, const char *delimiters)
     String Tokenizer.
     
    char * qstrunique (const char *seed)
     Generate unique id.
     
    char * qstr_comma_number (int number)
     Convert integer to comma string.
     
    char * qstrtrim (char *str)
     Remove white spaces(including CR, LF) from head and tail of the string. More...
     
    char * qstrtrim_head (char *str)
     Remove heading white spaces of the string. More...
     
    char * qstrtrim_tail (char *str)
     Remove tailing white spaces(including CR, LF) of the string. More...
     
    char * qstrunchar (char *str, char head, char tail)
     Remove character from head and tail of the string. More...
     
    char * qstrreplace (const char *mode, char *srcstr, const char *tokstr, const char *word)
     Replace string or tokens as word from source string with given mode. More...
     
    char * qstrcpy (char *dst, size_t size, const char *src)
     Copy src string to dst. More...
     
    char * qstrncpy (char *dst, size_t size, const char *src, size_t nbytes)
     Copy src string to dst no more than n bytes. More...
     
    char * qstrdupf (const char *format,...)
     Duplicate a formatted string. More...
     
    char * qstrdup_between (const char *str, const char *start, const char *end)
     Duplicate a substing set. More...
     
    void * qmemdup (const void *data, size_t size)
     Duplicate a copy of memory data. More...
     
    char * qstrcatf (char *str, const char *format,...)
     Append formatted string to the end of the source str. More...
     
    char * qstrgets (char *buf, size_t size, char **offset)
     Get one line from the string offset. More...
     
    char * qstrrev (char *str)
     Reverse the order of characters in the string. More...
     
    char * qstrupper (char *str)
     Convert character to bigger character. More...
     
    char * qstrlower (char *str)
     Convert character to lower character. More...
     
    char * qstrtok (char *str, const char *delimiters, char *retstop, int *offset)
     Split string into tokens. More...
     
    qlist_t * qstrtokenizer (const char *str, const char *delimiters)
     String Tokenizer. More...
     
    char * qstrunique (const char *seed)
     Generate unique id. More...
     
    char * qstr_comma_number (int number)
     Convert integer to comma string. More...
     
    bool qstrtest (int(*testfunc)(int), const char *str)
     Test for an alpha-numeric string.
     Test for an alpha-numeric string. More...
     
    bool qstr_is_email (const char *email)
     Test for an email-address formatted string.
     Test for an email-address formatted string. More...
     
    bool qstr_is_ip4addr (const char *str)
     Test for an IPv4 address string.
     Test for an IPv4 address string. More...
     
    char * qstr_conv_encoding (const char *str, const char *fromcode, const char *tocode, float mag)
     Convert character encoding.
     
    char * qstr_conv_encoding (const char *str, const char *fromcode, const char *tocode, float mag)
     Convert character encoding. More...
     

    Detailed Description

    String APIs.

    Definition in file qstring.c.

    Function Documentation

    - -

    ◆ qstrtrim()

    + +

    ◆ qstrtrim()

    - + @@ -177,14 +177,14 @@

    -

    ◆ qstrtrim_head()

    + +

    ◆ qstrtrim_head()

    char * qstrtrim char* qstrtrim ( char *  str)
    - + @@ -207,14 +207,14 @@

    -

    ◆ qstrtrim_tail()

    + +

    ◆ qstrtrim_tail()

    char * qstrtrim_head char* qstrtrim_head ( char *  str)
    - + @@ -237,14 +237,14 @@

    -

    ◆ qstrunchar()

    + +

    ◆ qstrunchar()

    char * qstrtrim_tail char* qstrtrim_tail ( char *  str)
    - + @@ -281,23 +281,23 @@

    Returns
    a pointer of source string if successful, otherwise returns NULL
    Note
    This modify source string directly.
    char *str = strdup(" \"hello world\" ");
    -
    qstrtrim(str); // to remove white spaces
    -
    qstrunchar(str, '"', '"'); // to unquote
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    -
    char * qstrunchar(char *str, char head, char tail)
    Remove character from head and tail of the string.
    Definition qstring.c:149
    +
    qstrtrim(str); // to remove white spaces
    +
    qstrunchar(str, '"', '"'); // to unquote
    +
    char * qstrunchar(char *str, char head, char tail)
    Remove character from head and tail of the string.
    Definition: qstring.c:149
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55

    Definition at line 149 of file qstring.c.

    - -

    ◆ qstrreplace()

    + +

    ◆ qstrreplace()

    char * qstrunchar char* qstrunchar ( char *  str,
    - + @@ -353,7 +353,7 @@

    strcpy(srcstr, "Welcome to The qDecoder Project.");
    printf("before %s : srcstr = %s\n", mode[i], srcstr);
    -
    retstr = qstrreplace(mode[i], srcstr, "The", "_");
    +
    retstr = qstrreplace(mode[i], srcstr, "The", "_");
    printf("after %s : srcstr = %s\n", mode[i], srcstr);
    printf(" retstr = %s\n\n", retstr);
    if(mode[i][1] == 'n') free(retstr);
    @@ -375,20 +375,20 @@

    before sr : srcstr = Welcome to The qDecoder Project.
    after sr : srcstr = Welcome to _ qDecoder Project.
    retstr = Welcome to _ qDecoder Project.
    -
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition qstring.c:236
    +
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition: qstring.c:236

    Definition at line 236 of file qstring.c.

    - -

    ◆ qstrcpy()

    + +

    ◆ qstrcpy()

    char * qstrreplace char* qstrreplace ( const char *  mode,
    - + @@ -429,14 +429,14 @@

    -

    ◆ qstrncpy()

    + +

    ◆ qstrncpy()

    char * qstrcpy char* qstrcpy ( char *  dst,
    - + @@ -484,14 +484,14 @@

    -

    ◆ qstrdupf()

    + +

    ◆ qstrdupf()

    char * qstrncpy char* qstrncpy ( char *  dst,
    - + @@ -523,14 +523,14 @@

    -

    ◆ qstrdup_between()

    + +

    ◆ qstrdup_between()

    char * qstrdupf char* qstrdupf ( const char *  format,
    - + @@ -570,14 +570,14 @@

    -

    ◆ qmemdup()

    + +

    ◆ qmemdup()

    char * qstrdup_between char* qstrdup_between ( const char *  str,
    - + @@ -610,14 +610,14 @@

    -

    ◆ qstrcatf()

    + +

    ◆ qstrcatf()

    void * qmemdup void* qmemdup ( const void *  data,
    - + @@ -656,14 +656,14 @@

    -

    ◆ qstrgets()

    + +

    ◆ qstrgets()

    char * qstrcatf char* qstrcatf ( char *  str,
    - + @@ -703,23 +703,23 @@

    char *offset = text;
    char buf[1024];
    -
    while(qstrgets(buf, sizeof(buf), &offset) == NULL) {
    +
    while(qstrgets(buf, sizeof(buf), &offset) == NULL) {
    printf("%s\n", buf);
    }
    -
    char * qstrgets(char *buf, size_t size, char **offset)
    Get one line from the string offset.
    Definition qstring.c:468
    +
    char * qstrgets(char *buf, size_t size, char **offset)
    Get one line from the string offset.
    Definition: qstring.c:468

    Definition at line 468 of file qstring.c.

    - -

    ◆ qstrrev()

    + +

    ◆ qstrrev()

    char * qstrgets char* qstrgets ( char *  buf,
    - + @@ -742,14 +742,14 @@

    -

    ◆ qstrupper()

    + +

    ◆ qstrupper()

    char * qstrrev char* qstrrev ( char *  str)
    - + @@ -772,14 +772,14 @@

    -

    ◆ qstrlower()

    + +

    ◆ qstrlower()

    char * qstrupper char* qstrupper ( char *  str)
    - + @@ -802,14 +802,14 @@

    -

    ◆ qstrtok()

    + +

    ◆ qstrtok()

    char * qstrlower char* qstrlower ( char *  str)
    - + @@ -854,24 +854,24 @@

    char *str = strdup("Hello,world|Thank,you");
    char *token;
    int offset = 0;
    -
    while((token = qstrtok(str, "|,", NULL, &offset)) != NULL) {
    +
    while((token = qstrtok(str, "|,", NULL, &offset)) != NULL) {
    printf("%s\n", token);
    }
    -
    char * qstrtok(char *str, const char *delimiters, char *retstop, int *offset)
    Split string into tokens.
    Definition qstring.c:583
    -
    Note
    This may modify str argument. The major difference between qstrtok() and standard strtok() is that qstrtok() can returns empty string tokens. If the str is "a:b::d", qstrtok() returns "a", "b", "", "d". But strtok() returns "a","b","d".
    +
    char * qstrtok(char *str, const char *delimiters, char *retstop, int *offset)
    Split string into tokens.
    Definition: qstring.c:583
    +
    Note
    This may modify str argument. The major difference between qstrtok() and standard strtok() is that qstrtok() can returns empty string tokens. If the str is "a:b::d", qstrtok() returns "a", "b", "", "d". But strtok() returns "a","b","d".

    Definition at line 583 of file qstring.c.

    - -

    ◆ qstrtokenizer()

    + +

    ◆ qstrtokenizer()

    char * qstrtok char* qstrtok ( char *  str,
    - + @@ -910,14 +910,14 @@

    -

    ◆ qstrunique()

    + +

    ◆ qstrunique()

    qlist_t * qstrtokenizer qlist_t* qstrtokenizer ( const char *  str,
    - + @@ -940,14 +940,14 @@

    -

    ◆ qstr_comma_number()

    + +

    ◆ qstr_comma_number()

    char * qstrunique char* qstrunique ( const char *  seed)
    - + @@ -969,8 +969,8 @@

    -

    ◆ qstrtest()

    + +

    ◆ qstrtest()

    @@ -1004,22 +1004,22 @@

    Returns
    true for ok, otherwise returns false
    -
    if(qstrtest(isalnum, "hello1234") == true) {
    +
    if(qstrtest(isalnum, "hello1234") == true) {
    printf("It is alpha-numeric string.");
    }
    -
    if(qstrtest(isdigit, "0123456789") == true) {
    +
    if(qstrtest(isdigit, "0123456789") == true) {
    printf("It is alpha-numeric string.");
    }
    -
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition qstring.c:752
    +
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition: qstring.c:752
    Note
    Basically you can use below test functios without creating your own version. Make sure <ctype.h> header should be included before using any of these functions. isalnum - checks for an alphanumeric character. isalpha - checks for an alphabetic character. isascii - checks whether c is a 7-bit unsigned char value that fits into the ASCII character set. isblank - checks for a blank character; that is, a space or a tab. iscntrl - checks for a control character. isdigit - checks for a digit (0 through 9). isgraph - checks for any printable character except space. islower - checks for a lower-case character. isprint - checks for any printable character including space. ispunct - checks for any printable character which is not a space or an alphanumeric character. isspace - checks for white-space characters. isupper - checks for an uppercase letter. isxdigit - checks for a hexadecimal digits. Please refer "man isalnum" for more details about these functions.

    Definition at line 752 of file qstring.c.

    - -

    ◆ qstr_is_email()

    + +

    ◆ qstr_is_email()

    @@ -1047,8 +1047,8 @@

    -

    ◆ qstr_is_ip4addr()

    + +

    ◆ qstr_is_ip4addr()

    @@ -1071,23 +1071,23 @@

    Returns
    true if successful, otherwise returns false
    -
    if(qstr_is_ip4addr("1.2.3.4") == true) {
    +
    if(qstr_is_ip4addr("1.2.3.4") == true) {
    printf("It is IPv4 address string.");
    }
    -
    bool qstr_is_ip4addr(const char *str)
    Test for an IPv4 address string.
    Definition qstring.c:825
    +
    bool qstr_is_ip4addr(const char *str)
    Test for an IPv4 address string.
    Definition: qstring.c:825

    Definition at line 825 of file qstring.c.

    - -

    ◆ qstr_conv_encoding()

    + +

    ◆ qstr_conv_encoding()

    char * qstr_comma_number char* qstr_comma_number ( int  number)
    - + @@ -1141,7 +1141,7 @@

    diff --git a/doc/html/qstring_8c.js b/doc/html/qstring_8c.js index 636b5acf..c0f95ec2 100644 --- a/doc/html/qstring_8c.js +++ b/doc/html/qstring_8c.js @@ -1,26 +1,26 @@ var qstring_8c = [ - [ "qstrtrim", "qstring_8c.html#a673d4ee449a08223e9f26508fa7d81a3", null ], - [ "qstrtrim_head", "qstring_8c.html#a70a69740b083dfe3116b2dd025aebdcc", null ], - [ "qstrtrim_tail", "qstring_8c.html#ab20a0316dfb2bc4f48c69d54fcb21355", null ], - [ "qstrunchar", "qstring_8c.html#a82ebc5e528abf5257099994578904b51", null ], - [ "qstrreplace", "qstring_8c.html#ac4aec98006a9b0f9eb54c8c43121425e", null ], - [ "qstrcpy", "qstring_8c.html#a24dcde4b707ed664b3962e1bf400de0f", null ], - [ "qstrncpy", "qstring_8c.html#a3be760da8b51532dad32d483bdbcf7b5", null ], - [ "qstrdupf", "qstring_8c.html#a90c6f8a88cb9de91d5e74fbd055d4551", null ], - [ "qstrdup_between", "qstring_8c.html#a86f97f3fb3a432af6fa1dd55cdc100bc", null ], - [ "qmemdup", "qstring_8c.html#aa27b0a87aa68e3ee318e3280a5f9d9d5", null ], - [ "qstrcatf", "qstring_8c.html#a728b578553bfb9fe6b88af5a26a95e2d", null ], - [ "qstrgets", "qstring_8c.html#abd9722d596de17775ff99107d124404c", null ], - [ "qstrrev", "qstring_8c.html#a019f74d6452ba69f93c1905a2c5363b2", null ], - [ "qstrupper", "qstring_8c.html#acc14c95a2ddadcd8d983d6e12d372d12", null ], - [ "qstrlower", "qstring_8c.html#ad42f6086a25ed4be54f2a18a8973dbd8", null ], - [ "qstrtok", "qstring_8c.html#a052c99b86a780692b68ba6cda90b477d", null ], - [ "qstrtokenizer", "qstring_8c.html#a38e5f0c9670254df2a1e4246e13d0c54", null ], - [ "qstrunique", "qstring_8c.html#a2e9c44c24a06d24bccc1967972c63909", null ], - [ "qstr_comma_number", "qstring_8c.html#ae5c7f3c367f90928d56b2db1cd60f646", null ], + [ "qstrtrim", "qstring_8c.html#aef51c584fd78c0430ccfb5f51c32cf71", null ], + [ "qstrtrim_head", "qstring_8c.html#a737037e2fc79cc1fa8de2a6fedaf21ed", null ], + [ "qstrtrim_tail", "qstring_8c.html#a1a4b63ebdee0be75f217d52dc7cb1750", null ], + [ "qstrunchar", "qstring_8c.html#aaab287c88286f42b65eeb52af24a3aa8", null ], + [ "qstrreplace", "qstring_8c.html#a01772aae3cdd533ff6f0f68367df9226", null ], + [ "qstrcpy", "qstring_8c.html#aa0fc982fbf9c6163d98cbb46e6faa86d", null ], + [ "qstrncpy", "qstring_8c.html#ae656735491546fa512358935c381e19a", null ], + [ "qstrdupf", "qstring_8c.html#afed8e04d6f83692b815251e9f76cc8fd", null ], + [ "qstrdup_between", "qstring_8c.html#a6ad659c9b3a4f6b515368cb0fa098d32", null ], + [ "qmemdup", "qstring_8c.html#acc32dbf9f419ef637d0547211b7125b9", null ], + [ "qstrcatf", "qstring_8c.html#a4ab535e3b8608f1ef81ed21a7d602cef", null ], + [ "qstrgets", "qstring_8c.html#abd1fa48bffd427c7c3847eebc9681f3b", null ], + [ "qstrrev", "qstring_8c.html#acc9b4062910be63934d6b1658359b9bd", null ], + [ "qstrupper", "qstring_8c.html#aa694e3f14941ac016fccbb456e24a134", null ], + [ "qstrlower", "qstring_8c.html#ad17ac23f4397125c8bdfd9359f2b6780", null ], + [ "qstrtok", "qstring_8c.html#a6183cbf7350c2373915feac84b5ec25c", null ], + [ "qstrtokenizer", "qstring_8c.html#af45ab0b307be585b9b24aa45e37ad5eb", null ], + [ "qstrunique", "qstring_8c.html#ad300f196f08a8243c384f2f59f06f67b", null ], + [ "qstr_comma_number", "qstring_8c.html#a75b5521fd90e830770182d68d613fc43", null ], [ "qstrtest", "qstring_8c.html#a6ddf2449ee1edb1139005fa88213567c", null ], [ "qstr_is_email", "qstring_8c.html#af8e066e1239a1670b9640bdf0e456b89", null ], [ "qstr_is_ip4addr", "qstring_8c.html#a87849863c09feb068f316ffb63aaef03", null ], - [ "qstr_conv_encoding", "qstring_8c.html#ae5ce5ba220fb8ad35c4434d54912f00b", null ] + [ "qstr_conv_encoding", "qstring_8c.html#ae5317b2503cfc75f1b7faaef86da33d6", null ] ]; \ No newline at end of file diff --git a/doc/html/qstring_8c_source.html b/doc/html/qstring_8c_source.html index 36cf4794..d40efd0d 100644 --- a/doc/html/qstring_8c_source.html +++ b/doc/html/qstring_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qstring.c Source File @@ -20,8 +20,8 @@

    char * qstr_conv_encoding char* qstr_conv_encoding ( const char *  str,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,950 +52,951 @@
    -
    qstring.c
    +
    +
    qstring.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qstring.c String APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <string.h>
    -
    37#include <stdarg.h>
    -
    38#include <ctype.h>
    -
    39#include <unistd.h>
    -
    40#include <sys/time.h>
    -
    41#include "qinternal.h"
    -
    42#include "utilities/qencode.h"
    -
    43#include "utilities/qhash.h"
    -
    44#include "utilities/qstring.h"
    -
    45
    -
    46/**
    -
    47 * Remove white spaces(including CR, LF) from head and tail of the string.
    -
    48 *
    -
    49 * @param str source string
    -
    50 *
    -
    51 * @return a pointer of source string if successful, otherwise returns NULL
    -
    52 *
    -
    53 * @note This modify source string directly.
    -
    54 */
    -
    55char *qstrtrim(char *str) {
    -
    56 if (str == NULL)
    -
    57 return NULL;
    -
    58
    -
    59 char *ss, *se;
    -
    60 for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
    -
    61 ss++)
    -
    62 ;
    -
    63 for (se = ss; *se != '\0'; se++)
    -
    64 ;
    -
    65 for (se--;
    -
    66 se >= ss
    -
    67 && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
    -
    68 se--)
    -
    69 ;
    -
    70 se++;
    -
    71 *se = '\0';
    -
    72
    -
    73 if (ss > str) {
    -
    74 size_t len = (se - ss) + 1;
    -
    75 memmove(str, ss, len);
    -
    76 }
    -
    77
    -
    78 return str;
    -
    79}
    -
    80
    -
    81/**
    -
    82 * Remove heading white spaces of the string.
    -
    83 *
    -
    84 * @param str source string
    -
    85 *
    -
    86 * @return a pointer of source string if successful, otherwise returns NULL
    -
    87 *
    -
    88 * @note This modify source string directly.
    -
    89 */
    -
    90char *qstrtrim_head(char *str) {
    -
    91 if (str == NULL)
    -
    92 return NULL;
    -
    93
    -
    94 char *ss;
    -
    95 for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
    -
    96 ss++)
    -
    97 ;
    -
    98
    -
    99 if (ss > str) {
    -
    100 size_t len = strlen(ss) + 1;
    -
    101 memmove(str, ss, len);
    -
    102 }
    -
    103
    -
    104 return str;
    -
    105}
    -
    106
    -
    107/**
    -
    108 * Remove tailing white spaces(including CR, LF) of the string.
    -
    109 *
    -
    110 * @param str source string
    -
    111 *
    -
    112 * @return a pointer of source string if successful, otherwise returns NULL
    -
    113 *
    -
    114 * @note This modify source string directly.
    -
    115 */
    -
    116char *qstrtrim_tail(char *str) {
    -
    117 if (str == NULL)
    -
    118 return NULL;
    -
    119
    -
    120 char *se;
    -
    121 for (se = str + strlen(str) - 1;
    -
    122 se >= str
    -
    123 && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
    -
    124 se--)
    -
    125 ;
    -
    126 se++;
    -
    127 *se = '\0';
    -
    128
    -
    129 return str;
    -
    130}
    -
    131
    -
    132/**
    -
    133 * Remove character from head and tail of the string.
    -
    134 *
    -
    135 * @param str source string
    -
    136 * @param head heading character
    -
    137 * @param tail tailing character
    -
    138 *
    -
    139 * @return a pointer of source string if successful, otherwise returns NULL
    -
    140 *
    -
    141 * @note This modify source string directly.
    -
    142 *
    -
    143 * @code
    -
    144 * char *str = strdup(" \"hello world\" ");
    -
    145 * qstrtrim(str); // to remove white spaces
    -
    146 * qstrunchar(str, '"', '"'); // to unquote
    -
    147 * @endcode
    -
    148 */
    -
    149char *qstrunchar(char *str, char head, char tail) {
    -
    150 if (str == NULL)
    -
    151 return NULL;
    -
    152
    -
    153 int len = strlen(str);
    -
    154 if (len >= 2 && str[0] == head && str[len - 1] == tail) {
    -
    155 memmove(str, str + 1, len - 2);
    -
    156 str[len - 2] = '\0';
    -
    157 } else {
    -
    158 return NULL;
    -
    159 }
    -
    160
    -
    161 return str;
    -
    162}
    -
    163
    -
    164/**
    -
    165 * Replace string or tokens as word from source string with given mode.
    -
    166 *
    -
    167 * @param mode replacing mode
    -
    168 * @param srcstr source string
    -
    169 * @param tokstr token or string
    -
    170 * @param word target word to be replaced
    -
    171 *
    -
    172 * @return a pointer of malloced or source string depending on the mode if
    -
    173 * successful, otherwise returns NULL
    -
    174 *
    -
    175 * @note
    -
    176 * The mode argument has two separated characters. First character
    -
    177 * is used to decide replacing method and can be 't' or 's'.
    -
    178 * The character 't' and 's' stand on [t]oken and [s]tring.
    -
    179 *
    -
    180 * When 't' is given each character of the token string(third argument)
    -
    181 * will be compared with source string individually. If matched one
    -
    182 * is found. the character will be replaced with given work.
    -
    183 *
    -
    184 * If 's' is given instead of 't'. Token string will be analyzed
    -
    185 * only one chunk word. So the replacement will be occured when
    -
    186 * the case of whole word matched.
    -
    187 *
    -
    188 * Second character is used to decide returning memory type and
    -
    189 * can be 'n' or 'r' which are stand on [n]ew and [r]eplace.
    -
    190 *
    -
    191 * When 'n' is given the result will be placed into new array so
    -
    192 * you should free the return string after using. Instead of this,
    -
    193 * you can also use 'r' character to modify source string directly.
    -
    194 * In this case, given source string should have enough space. Be
    -
    195 * sure that untouchable value can not be used for source string.
    -
    196 *
    -
    197 * So there are four associatable modes such like below.
    -
    198 *
    -
    199 * Mode "tn" : [t]oken replacing & putting the result into [n]ew array.
    -
    200 * Mode "tr" : [t]oken replacing & [r]eplace source string directly.
    -
    201 * Mode "sn" : [s]tring replacing & putting the result into [n]ew array.
    -
    202 * Mode "sr" : [s]tring replacing & [r]eplace source string directly.
    -
    203 *
    -
    204 * @code
    -
    205 * char srcstr[256], *retstr;
    -
    206 * char mode[4][2+1] = {"tn", "tr", "sn", "sr"};
    -
    207 *
    -
    208 * for(i = 0; i < 4; i++) {
    -
    209 * strcpy(srcstr, "Welcome to The qDecoder Project.");
    -
    210 *
    -
    211 * printf("before %s : srcstr = %s\n", mode[i], srcstr);
    -
    212 * retstr = qstrreplace(mode[i], srcstr, "The", "_");
    -
    213 * printf("after %s : srcstr = %s\n", mode[i], srcstr);
    -
    214 * printf(" retstr = %s\n\n", retstr);
    -
    215 * if(mode[i][1] == 'n') free(retstr);
    -
    216 * }
    -
    217 *
    -
    218 * --[Result]--
    -
    219 * before tn : srcstr = Welcome to The qDecoder Project.
    -
    220 * after tn : srcstr = Welcome to The qDecoder Project.
    -
    221 * retstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    -
    222 *
    -
    223 * before tr : srcstr = Welcome to The qDecoder Project.
    -
    224 * after tr : srcstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    -
    225 * retstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    -
    226 *
    -
    227 * before sn : srcstr = Welcome to The qDecoder Project.
    -
    228 * after sn : srcstr = Welcome to The qDecoder Project.
    -
    229 * retstr = Welcome to _ qDecoder Project.
    -
    230 *
    -
    231 * before sr : srcstr = Welcome to The qDecoder Project.
    -
    232 * after sr : srcstr = Welcome to _ qDecoder Project.
    -
    233 * retstr = Welcome to _ qDecoder Project.
    -
    234 * @endcode
    -
    235 */
    -
    236char *qstrreplace(const char *mode, char *srcstr, const char *tokstr,
    -
    237 const char *word) {
    -
    238 if (mode == NULL || strlen(mode) != 2|| srcstr == NULL || tokstr == NULL
    -
    239 || word == NULL) {
    -
    240 DEBUG("Unknown mode \"%s\".", mode);
    -
    241 return NULL;
    -
    242 }
    -
    243
    -
    244 char *newstr, *newp, *srcp, *tokenp, *retp;
    -
    245 newstr = newp = srcp = tokenp = retp = NULL;
    -
    246
    -
    247 char method = mode[0], memuse = mode[1];
    -
    248 int maxstrlen, tokstrlen;
    -
    249
    -
    250 /* Put replaced string into malloced 'newstr' */
    -
    251 if (method == 't') { /* Token replace */
    -
    252 maxstrlen = strlen(srcstr) * ((strlen(word) > 0) ? strlen(word) : 1);
    -
    253 newstr = (char *) malloc(maxstrlen + 1);
    -
    254 if (newstr == NULL)
    -
    255 return NULL;
    -
    256
    -
    257 for (srcp = (char *) srcstr, newp = newstr; *srcp; srcp++) {
    -
    258 for (tokenp = (char *) tokstr; *tokenp; tokenp++) {
    -
    259 if (*srcp == *tokenp) {
    -
    260 char *wordp;
    -
    261 for (wordp = (char *) word; *wordp; wordp++) {
    -
    262 *newp++ = *wordp;
    -
    263 }
    -
    264 break;
    -
    265 }
    -
    266 }
    -
    267 if (!*tokenp)
    -
    268 *newp++ = *srcp;
    -
    269 }
    -
    270 *newp = '\0';
    -
    271 } else if (method == 's') { /* String replace */
    -
    272 if (strlen(word) > strlen(tokstr)) {
    -
    273 maxstrlen = ((strlen(srcstr) / strlen(tokstr)) * strlen(word))
    -
    274 + (strlen(srcstr) % strlen(tokstr));
    -
    275 } else {
    -
    276 maxstrlen = strlen(srcstr);
    -
    277 }
    -
    278 newstr = (char *) malloc(maxstrlen + 1);
    -
    279 if (newstr == NULL)
    -
    280 return NULL;
    -
    281
    -
    282 tokstrlen = strlen(tokstr);
    -
    283
    -
    284 for (srcp = srcstr, newp = newstr; *srcp; srcp++) {
    -
    285 if (!strncmp(srcp, tokstr, tokstrlen)) {
    -
    286 char *wordp;
    -
    287 for (wordp = (char *) word; *wordp; wordp++)
    -
    288 *newp++ = *wordp;
    -
    289 srcp += tokstrlen - 1;
    -
    290 } else
    -
    291 *newp++ = *srcp;
    -
    292 }
    -
    293 *newp = '\0';
    -
    294 } else {
    -
    295 DEBUG("Unknown mode \"%s\".", mode);
    -
    296 return NULL;
    -
    297 }
    -
    298
    -
    299 /* decide whether newing the memory or replacing into exist one */
    -
    300 if (memuse == 'n')
    -
    301 retp = newstr;
    -
    302 else if (memuse == 'r') {
    -
    303 strcpy(srcstr, newstr);
    -
    304 free(newstr);
    -
    305 retp = srcstr;
    -
    306 } else {
    -
    307 DEBUG("Unknown mode \"%s\".", mode);
    -
    308 free(newstr);
    -
    309 return NULL;
    -
    310 }
    -
    311
    -
    312 return retp;
    -
    313}
    -
    314
    -
    315/**
    -
    316 * Copy src string to dst. The dst string array will be always terminated by
    -
    317 * NULL character. Also allows overlap between src and dst.
    -
    318 *
    -
    319 * @param dst a pointer of the string to be copied
    -
    320 * @param size the size of dst character arrary
    -
    321 * @param src a pointer of source string
    -
    322 *
    -
    323 * @return always returns a pointer of dst
    -
    324 */
    -
    325char *qstrcpy(char *dst, size_t size, const char *src) {
    -
    326 if (dst == NULL || size == 0 || src == NULL)
    -
    327 return dst;
    -
    328
    -
    329 size_t nbytes = strlen(src);
    -
    330 return qstrncpy(dst, size, src, nbytes);
    -
    331}
    -
    332
    -
    333/**
    -
    334 * Copy src string to dst no more than n bytes. The dst string array will be
    -
    335 * always terminated by NULL character. Also allows overlap between src and dst.
    -
    336 *
    -
    337 * @param dst a pointer of the string to be copied
    -
    338 * @param size the size of dst character arrary
    -
    339 * @param src a pointer of source string
    -
    340 * @param nbytes number of bytes to copy
    -
    341 *
    -
    342 * @return always returns a pointer of dst
    -
    343 */
    -
    344char *qstrncpy(char *dst, size_t size, const char *src, size_t nbytes) {
    -
    345 if (dst == NULL || size == 0 || src == NULL)
    -
    346 return dst;
    -
    347
    -
    348 if (nbytes >= size)
    -
    349 nbytes = size - 1;
    -
    350 memmove((void *) dst, (void *) src, nbytes);
    -
    351 dst[nbytes] = '\0';
    -
    352
    -
    353 return dst;
    -
    354}
    -
    355
    -
    356/**
    -
    357 * Duplicate a formatted string.
    -
    358 *
    -
    359 * @param format string format
    -
    360 *
    -
    361 * @return a pointer of malloced string if successful, otherwise returns NULL
    -
    362 */
    -
    363char *qstrdupf(const char *format, ...) {
    -
    364 char *str;
    -
    365 DYNAMIC_VSPRINTF(str, format);
    -
    366 if (str == NULL)
    -
    367 return NULL;
    -
    368
    -
    369 char *dup = strdup(str);
    -
    370 free(str);
    -
    371
    -
    372 return dup;
    -
    373}
    -
    374
    -
    375/**
    -
    376 * Duplicate a substing set
    -
    377 *
    -
    378 * @param str a pointer of original string
    -
    379 * @param start substring which is started with this
    -
    380 * @param end substring which is ended with this
    -
    381 *
    -
    382 * @return a pointer of malloced string if successful, otherwise returns NULL
    -
    383 */
    -
    384char *qstrdup_between(const char *str, const char *start, const char *end) {
    -
    385 char *s;
    -
    386 if ((s = strstr(str, start)) == NULL)
    -
    387 return NULL;
    -
    388 s += strlen(start);
    -
    389
    -
    390 char *e;
    -
    391 if ((e = strstr(s, end)) == NULL)
    -
    392 return NULL;
    -
    393
    -
    394 int len = e - s;
    -
    395
    -
    396 char *buf = (char *) malloc(sizeof(char) * (len + 1));
    -
    397 if (buf == NULL)
    -
    398 return NULL;
    -
    399
    -
    400 strncpy(buf, s, len);
    -
    401 buf[len] = '\0';
    -
    402 return buf;
    -
    403}
    -
    404
    -
    405/**
    -
    406 * Duplicate a copy of memory data.
    -
    407 *
    -
    408 * @param data source data
    -
    409 * @param size data size
    -
    410 *
    -
    411 * @return a pointer of malloced data which's content is identical to source data.
    -
    412 */
    -
    413void *qmemdup(const void *data, size_t size) {
    -
    414 if (data == NULL || size == 0) {
    -
    415 return NULL;
    -
    416 }
    -
    417
    -
    418 void *newdata = malloc(size);
    -
    419 if (newdata == NULL) {
    -
    420 return NULL;
    -
    421 }
    -
    422
    -
    423 memcpy(newdata, data, size);
    -
    424 return newdata;
    -
    425}
    -
    426
    -
    427/**
    -
    428 * Append formatted string to the end of the source str
    -
    429 *
    -
    430 * @param str a pointer of original string
    -
    431 * @param format string format to append
    -
    432 *
    -
    433 * @return a pointer of str if successful, otherwise returns NULL
    -
    434 */
    -
    435char *qstrcatf(char *str, const char *format, ...) {
    -
    436 char *buf;
    -
    437 DYNAMIC_VSPRINTF(buf, format);
    -
    438 if (buf == NULL)
    -
    439 return NULL;
    -
    440
    -
    441 char *ret = strcat(str, buf);
    -
    442 free(buf);
    -
    443 return ret;
    -
    444}
    -
    445
    -
    446/**
    -
    447 * Get one line from the string offset.
    -
    448 *
    -
    449 * @param buf buffer pointer
    -
    450 * @param size buffer size
    -
    451 * @param offset a offset pointer which point source string
    -
    452 *
    -
    453 * @return a pointer of buffer if successful, otherwise(EOF) returns NULL
    -
    454 *
    -
    455 * @note
    -
    456 * CR and LF will not be stored.
    -
    457 *
    -
    458 * @code
    -
    459 * char *text="Hello\nWorld";
    -
    460 *
    -
    461 * char *offset = text;
    -
    462 * char buf[1024];
    -
    463 * while(qstrgets(buf, sizeof(buf), &offset) == NULL) {
    -
    464 * printf("%s\n", buf);
    -
    465 * }
    -
    466 * @endcode
    -
    467 */
    -
    468char *qstrgets(char *buf, size_t size, char **offset) {
    -
    469 if (offset == NULL || *offset == NULL || **offset == '\0')
    -
    470 return NULL;
    -
    471
    -
    472 size_t i;
    -
    473 char *from = *offset;
    -
    474 char *to = buf;
    -
    475 for (i = 0; *from != '\0' && i < (size - 1); i++, from++) {
    -
    476 if (*from == '\r')
    -
    477 continue;
    -
    478 if (*from == '\n') {
    -
    479 from++;
    -
    480 break;
    -
    481 }
    -
    482 *to = *from;
    -
    483 to++;
    -
    484 }
    -
    485 *to = '\0';
    -
    486 *offset = from;
    -
    487
    -
    488 return buf;
    -
    489}
    -
    490
    -
    491/**
    -
    492 * Reverse the order of characters in the string
    -
    493 *
    -
    494 * @param str a pointer of source string
    -
    495 *
    -
    496 * @return always returns a pointer of str
    -
    497 *
    -
    498 * @note This modify str directly.
    -
    499 */
    -
    500char *qstrrev(char *str) {
    -
    501 if (str == NULL)
    -
    502 return str;
    -
    503
    -
    504 char *p1, *p2;
    -
    505 for (p1 = str, p2 = str + (strlen(str) - 1); p2 > p1; p1++, p2--) {
    -
    506 char t = *p1;
    -
    507 *p1 = *p2;
    -
    508 *p2 = t;
    -
    509 }
    -
    510
    -
    511 return str;
    -
    512}
    -
    513
    -
    514/**
    -
    515 * Convert character to bigger character.
    -
    516 *
    -
    517 * @param str a pointer of source string
    -
    518 *
    -
    519 * @return always returns a pointer of str
    -
    520 *
    -
    521 * @note This modify str directly.
    -
    522 */
    -
    523char *qstrupper(char *str) {
    -
    524 char *cp;
    -
    525
    -
    526 if (!str)
    -
    527 return NULL;
    -
    528 for (cp = str; *cp; cp++)
    -
    529 if (*cp >= 'a' && *cp <= 'z')
    -
    530 *cp -= 32;
    -
    531 return str;
    -
    532}
    -
    533
    -
    534/**
    -
    535 * Convert character to lower character.
    -
    536 *
    -
    537 * @param str a pointer of source string
    -
    538 *
    -
    539 * @return always returns a pointer of str
    -
    540 *
    -
    541 * @note This modify str directly.
    -
    542 */
    -
    543char *qstrlower(char *str) {
    -
    544 char *cp;
    -
    545
    -
    546 if (!str)
    -
    547 return NULL;
    -
    548 for (cp = str; *cp; cp++)
    -
    549 if (*cp >= 'A' && *cp <= 'Z')
    -
    550 *cp += 32;
    -
    551 return str;
    -
    552}
    -
    553
    -
    554/**
    -
    555 * Split string into tokens
    -
    556 *
    -
    557 * @param str source string
    -
    558 * @param delimiters string that specifies a set of delimiters that may
    -
    559 * surround the token being extracted
    -
    560 * @param retstop stop delimiter character will be stored. it can be NULL
    -
    561 * if you don't want to know.
    -
    562 * @param offset integer pointer used for store last position.
    -
    563 * (must be reset to 0)
    -
    564 *
    -
    565 * @return a pointer to the first byte of a token if successful, otherwise
    -
    566 * returns NULL.
    -
    567 *
    -
    568 * @code
    -
    569 * char *str = strdup("Hello,world|Thank,you");
    -
    570 * char *token;
    -
    571 * int offset = 0;
    -
    572 * while((token = qstrtok(str, "|,", NULL, &offset)) != NULL) {
    -
    573 * printf("%s\n", token);
    -
    574 * }
    -
    575 * @endcode
    -
    576 *
    -
    577 * @note
    -
    578 * This may modify str argument.
    -
    579 * The major difference between qstrtok() and standard strtok() is that
    -
    580 * qstrtok() can returns empty string tokens. If the str is "a:b::d", qstrtok()
    -
    581 * returns "a", "b", "", "d". But strtok() returns "a","b","d".
    -
    582 */
    -
    583char *qstrtok(char *str, const char *delimiters, char *retstop, int *offset) {
    -
    584 char *tokensp, *tokenep;
    -
    585
    -
    586 tokensp = tokenep = (char *) (str + *offset);
    -
    587 int numdel = strlen(delimiters);
    -
    588 for (; *tokenep; tokenep++) {
    -
    589 int j;
    -
    590 for (j = 0; j < numdel; j++) {
    -
    591 if (*tokenep == delimiters[j]) {
    -
    592 if (retstop != NULL)
    -
    593 *retstop = delimiters[j];
    -
    594 *tokenep = '\0';
    -
    595 tokenep++;
    -
    596 *offset = tokenep - str;
    -
    597 return tokensp;
    -
    598 }
    -
    599 }
    -
    600 }
    -
    601
    -
    602 if (retstop != NULL)
    -
    603 *retstop = '\0';
    -
    604 if (tokensp != tokenep) {
    -
    605 *offset = tokenep - str;
    -
    606 return tokensp;
    -
    607 }
    -
    608 return NULL;
    -
    609}
    -
    610
    -
    611/**
    -
    612 * String Tokenizer
    -
    613 *
    -
    614 * @param str source string
    -
    615 * @param delimiters string that specifies a set of delimiters that may
    -
    616 * surround the token being extracted
    -
    617 *
    -
    618 * @return qlist container pointer otherwise returns NULL.
    -
    619 *
    -
    620 * @code
    -
    621 * qlist_t *tokens = qstr_tokenizer("a:b:c", ":");
    -
    622 * char *str;
    -
    623 * while((str = tokens->popfirst(tokens, NULL)) != NULL) {
    -
    624 * printf("%s\n", str);
    -
    625 * }
    -
    626 * tokens->free(tokens);
    -
    627 * @endcode
    -
    628 */
    -
    629qlist_t *qstrtokenizer(const char *str, const char *delimiters) {
    -
    630 qlist_t *list = qlist(0);
    -
    631 if (list == NULL)
    -
    632 return NULL;
    -
    633
    -
    634 int i;
    -
    635 char *dupstr = strdup(str);
    -
    636 char *token;
    -
    637 int offset = 0;
    -
    638 for (i = 1, token = qstrtok(dupstr, delimiters, NULL, &offset);
    -
    639 token != NULL;
    -
    640 token = qstrtok(dupstr, delimiters, NULL, &offset), i++) {
    -
    641 list->addlast(list, token, strlen(token) + 1);
    -
    642 }
    -
    643 free(dupstr);
    -
    644
    -
    645 return list;
    -
    646}
    -
    647
    -
    648/**
    -
    649 * Generate unique id
    -
    650 *
    -
    651 * @param seed additional seed string. this can be NULL
    -
    652 *
    -
    653 * @return a pointer of malloced string
    -
    654 *
    -
    655 * @note
    -
    656 * The length of returned string is 32+1 bytes long including terminating NULL
    -
    657 * character. It's a good idea to call srand() once before calling this because
    -
    658 * it uses rand().
    -
    659 */
    -
    660char *qstrunique(const char *seed) {
    -
    661 long usec;
    -
    662#ifdef _WIN32
    -
    663 FILETIME ft;
    -
    664 GetSystemTimeAsFileTime(&ft);
    -
    665 usec = ft.dwLowDateTime % 1000000;
    -
    666#else
    -
    667 struct timeval tv;
    -
    668 gettimeofday(&tv, NULL);
    -
    669 usec = tv.tv_usec;
    -
    670#endif
    -
    671
    -
    672 char uniquestr[128];
    -
    673 snprintf(uniquestr, sizeof(uniquestr), "%u%d%lu%ld%s", getpid(), rand(),
    -
    674 (unsigned long)time(NULL), usec, (seed != NULL) ? seed : "");
    -
    675
    -
    676 unsigned char md5hash[16];
    -
    677 qhashmd5(uniquestr, strlen(uniquestr), md5hash);
    -
    678 char *md5ascii = qhex_encode(md5hash, 16);
    -
    679
    -
    680 return md5ascii;
    -
    681}
    -
    682
    -
    683/**
    -
    684 * Convert integer to comma string.
    -
    685 *
    -
    686 * @param number integer
    -
    687 *
    -
    688 * @return a pointer of malloced string which contains comma separated number
    -
    689 * if successful, otherwise returns NULL
    -
    690 */
    -
    691char *qstr_comma_number(int number) {
    -
    692 char *str, *strp;
    -
    693
    -
    694 str = strp = (char *) malloc(sizeof(char) * (14 + 1));
    -
    695 if (str == NULL)
    -
    696 return NULL;
    -
    697
    -
    698 char buf[10 + 1], *bufp;
    -
    699 snprintf(buf, sizeof(buf), "%d", abs(number));
    -
    700
    -
    701 if (number < 0)
    -
    702 *strp++ = '-';
    -
    703 for (bufp = buf; *bufp != '\0'; strp++, bufp++) {
    -
    704 *strp = *bufp;
    -
    705 if ((strlen(bufp)) % 3 == 1 && *(bufp + 1) != '\0')
    -
    706 *(++strp) = ',';
    -
    707 }
    -
    708 *strp = '\0';
    -
    709
    -
    710 return str;
    -
    711}
    -
    712
    -
    713/**
    -
    714 * Test for an alpha-numeric string
    -
    715 *
    -
    716 * @param testfunc test function for individual character
    -
    717 * @param str a pointer of string
    -
    718 *
    -
    719 * @return true for ok, otherwise returns false
    -
    720 *
    -
    721 * @code
    -
    722 * if(qstrtest(isalnum, "hello1234") == true) {
    -
    723 * printf("It is alpha-numeric string.");
    -
    724 * }
    -
    725 *
    -
    726 * if(qstrtest(isdigit, "0123456789") == true) {
    -
    727 * printf("It is alpha-numeric string.");
    -
    728 * }
    -
    729 * @endcode
    -
    730 *
    -
    731 * @note
    -
    732 * Basically you can use below test functios without creating your own version.
    -
    733 * Make sure <ctype.h> header should be included before using any of these
    -
    734 * functions.
    -
    735 * isalnum - checks for an alphanumeric character.
    -
    736 * isalpha - checks for an alphabetic character.
    -
    737 * isascii - checks whether c is a 7-bit unsigned char value that fits into
    -
    738 * the ASCII character set.
    -
    739 * isblank - checks for a blank character; that is, a space or a tab.
    -
    740 * iscntrl - checks for a control character.
    -
    741 * isdigit - checks for a digit (0 through 9).
    -
    742 * isgraph - checks for any printable character except space.
    -
    743 * islower - checks for a lower-case character.
    -
    744 * isprint - checks for any printable character including space.
    -
    745 * ispunct - checks for any printable character which is not a space or an
    -
    746 * alphanumeric character.
    -
    747 * isspace - checks for white-space characters.
    -
    748 * isupper - checks for an uppercase letter.
    -
    749 * isxdigit - checks for a hexadecimal digits.
    -
    750 * Please refer "man isalnum" for more details about these functions.
    -
    751 */
    -
    752bool qstrtest(int (*testfunc)(int), const char *str) {
    -
    753 for (; *str; str++) {
    -
    754 if (testfunc(*str) == 0)
    -
    755 return false;
    -
    756 }
    -
    757 return true;
    -
    758}
    -
    759
    -
    760/**
    -
    761 * Test for an email-address formatted string
    -
    762 *
    -
    763 * @param email email-address formatted string
    -
    764 *
    -
    765 * @return true if successful, otherwise returns false
    -
    766 */
    -
    767bool qstr_is_email(const char *email) {
    -
    768 int i, alpa, dot, gol;
    -
    769
    -
    770 if (email == NULL)
    -
    771 return false;
    -
    772
    -
    773 for (i = alpa = dot = gol = 0; email[i] != '\0'; i++) {
    -
    774 switch (email[i]) {
    -
    775 case '@': {
    -
    776 if (alpa == 0)
    -
    777 return false;
    -
    778 if (gol > 0)
    -
    779 return false;
    -
    780 gol++;
    -
    781 break;
    -
    782 }
    -
    783 case '.': {
    -
    784 if ((i > 0) && (email[i - 1] == '@'))
    -
    785 return false;
    -
    786 if ((gol > 0) && (email[i - 1] == '.'))
    -
    787 return false;
    -
    788 dot++;
    -
    789 break;
    -
    790 }
    -
    791 default: {
    -
    792 alpa++;
    -
    793 if ((email[i] >= '0') && (email[i] <= '9'))
    -
    794 break;
    -
    795 else if ((email[i] >= 'A') && (email[i] <= 'Z'))
    -
    796 break;
    -
    797 else if ((email[i] >= 'a') && (email[i] <= 'z'))
    -
    798 break;
    -
    799 else if ((email[i] == '-') || (email[i] == '_'))
    -
    800 break;
    -
    801 else
    -
    802 return false;
    -
    803 }
    -
    804 }
    -
    805 }
    -
    806
    -
    807 if ((alpa <= 3) || (gol == 0) || (dot == 0))
    -
    808 return false;
    -
    809 return true;
    -
    810}
    -
    811
    -
    812/**
    -
    813 * Test for an IPv4 address string
    -
    814 *
    -
    815 * @param url IPv4 address string
    -
    816 *
    -
    817 * @return true if successful, otherwise returns false
    -
    818 *
    -
    819 * @code
    -
    820 * if(qstr_is_ip4addr("1.2.3.4") == true) {
    -
    821 * printf("It is IPv4 address string.");
    -
    822 * }
    -
    823 * @endcode
    -
    824 */
    -
    825bool qstr_is_ip4addr(const char *str) {
    -
    826 char *dupstr = strdup(str);
    -
    827
    -
    828 char *s1, *s2;
    -
    829 int periodcnt;
    -
    830 for (s1 = dupstr, periodcnt = 0; (s2 = strchr(s1, '.')) != NULL;
    -
    831 s1 = s2 + 1, periodcnt++) {
    -
    832 *s2 = '\0';
    -
    833
    -
    834 int n;
    -
    835 if (qstrtest(isdigit, s1) == false || (n = atoi(s1)) <= 0 || n >= 256) {
    -
    836 free(dupstr);
    -
    837 return false;
    -
    838 }
    -
    839 }
    -
    840
    -
    841 free(dupstr);
    -
    842 if (periodcnt != 3)
    -
    843 return false;
    -
    844 return true;
    -
    845}
    -
    846
    -
    847#ifdef __linux__
    -
    848#include <iconv.h>
    -
    849#endif
    -
    850
    -
    851/**
    -
    852 * Convert character encoding
    -
    853 *
    -
    854 * @param str additional seed string. this can be NULL
    -
    855 * @param fromcode encoding type of str
    -
    856 * @param tocode encoding to convert
    -
    857 * @param mag magnification between fromcode and tocode
    -
    858 *
    -
    859 * @return a pointer of malloced converted string if successful,
    -
    860 * otherwise returns NULL
    -
    861 *
    -
    862 * @code
    -
    863 * qCharEncode("KOREAN_EUCKR_STRING", "EUC-KR", "UTF-8", 1.5);
    -
    864 * @endcode
    -
    865 */
    -
    866char *qstr_conv_encoding(const char *str, const char *fromcode,
    -
    867 const char *tocode, float mag) {
    -
    868#ifdef __linux__
    -
    869 if (str == NULL)
    -
    870 return NULL;
    -
    871
    -
    872 char *fromstr = (char *) str;
    -
    873 size_t fromsize = strlen(fromstr) + 1;
    -
    874
    -
    875 size_t tosize = sizeof(char) * ((mag * (fromsize - 1)) + 1);
    -
    876 char *tostr = (char *) malloc(tosize);
    -
    877 if (tostr == NULL)
    -
    878 return NULL;
    -
    879
    -
    880 char *tostr1 = tostr;
    -
    881
    -
    882 iconv_t it = iconv_open(tocode, fromcode);
    -
    883 if (it < 0) {
    -
    884 DEBUG("iconv_open() failed.");
    -
    885 return NULL;
    -
    886 }
    -
    887
    -
    888 int ret = iconv(it, &fromstr, &fromsize, &tostr, &tosize);
    -
    889 iconv_close(it);
    -
    890
    -
    891 if (ret < 0) {
    -
    892 DEBUG("iconv() failed.");
    -
    893 free(tostr1);
    -
    894 return NULL;
    -
    895 }
    -
    896
    -
    897 return tostr1;
    -
    898#else
    -
    899 return NULL;
    -
    900#endif
    -
    901}
    -
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition qencode.c:393
    -
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition qhash.c:67
    -
    static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->head(): Sends a HEAD request.
    -
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition qlist.c:124
    -
    char * qstrrev(char *str)
    Reverse the order of characters in the string.
    Definition qstring.c:500
    -
    char * qstrtok(char *str, const char *delimiters, char *retstop, int *offset)
    Split string into tokens.
    Definition qstring.c:583
    -
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition qstring.c:325
    -
    char * qstrunique(const char *seed)
    Generate unique id.
    Definition qstring.c:660
    -
    qlist_t * qstrtokenizer(const char *str, const char *delimiters)
    String Tokenizer.
    Definition qstring.c:629
    -
    char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
    Copy src string to dst no more than n bytes.
    Definition qstring.c:344
    -
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition qstring.c:55
    -
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition qstring.c:752
    -
    char * qstrtrim_head(char *str)
    Remove heading white spaces of the string.
    Definition qstring.c:90
    -
    char * qstrcatf(char *str, const char *format,...)
    Append formatted string to the end of the source str.
    Definition qstring.c:435
    -
    char * qstrunchar(char *str, char head, char tail)
    Remove character from head and tail of the string.
    Definition qstring.c:149
    -
    char * qstrdup_between(const char *str, const char *start, const char *end)
    Duplicate a substing set.
    Definition qstring.c:384
    -
    bool qstr_is_ip4addr(const char *str)
    Test for an IPv4 address string.
    Definition qstring.c:825
    -
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition qstring.c:363
    -
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition qstring.c:413
    -
    char * qstrtrim_tail(char *str)
    Remove tailing white spaces(including CR, LF) of the string.
    Definition qstring.c:116
    -
    char * qstrgets(char *buf, size_t size, char **offset)
    Get one line from the string offset.
    Definition qstring.c:468
    -
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition qstring.c:236
    -
    char * qstrupper(char *str)
    Convert character to bigger character.
    Definition qstring.c:523
    -
    char * qstrlower(char *str)
    Convert character to lower character.
    Definition qstring.c:543
    -
    char * qstr_comma_number(int number)
    Convert integer to comma string.
    Definition qstring.c:691
    -
    char * qstr_conv_encoding(const char *str, const char *fromcode, const char *tocode, float mag)
    Convert character encoding.
    Definition qstring.c:866
    -
    bool qstr_is_email(const char *email)
    Test for an email-address formatted string.
    Definition qstring.c:767
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qstring.c String APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <string.h>
    +
    37 #include <stdarg.h>
    +
    38 #include <ctype.h>
    +
    39 #include <unistd.h>
    +
    40 #include <sys/time.h>
    +
    41 #include "qinternal.h"
    +
    42 #include "utilities/qencode.h"
    +
    43 #include "utilities/qhash.h"
    +
    44 #include "utilities/qstring.h"
    +
    45 
    +
    46 /**
    +
    47  * Remove white spaces(including CR, LF) from head and tail of the string.
    +
    48  *
    +
    49  * @param str source string
    +
    50  *
    +
    51  * @return a pointer of source string if successful, otherwise returns NULL
    +
    52  *
    +
    53  * @note This modify source string directly.
    +
    54  */
    +
    55 char *qstrtrim(char *str) {
    +
    56  if (str == NULL)
    +
    57  return NULL;
    +
    58 
    +
    59  char *ss, *se;
    +
    60  for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
    +
    61  ss++)
    +
    62  ;
    +
    63  for (se = ss; *se != '\0'; se++)
    +
    64  ;
    +
    65  for (se--;
    +
    66  se >= ss
    +
    67  && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
    +
    68  se--)
    +
    69  ;
    +
    70  se++;
    +
    71  *se = '\0';
    +
    72 
    +
    73  if (ss > str) {
    +
    74  size_t len = (se - ss) + 1;
    +
    75  memmove(str, ss, len);
    +
    76  }
    +
    77 
    +
    78  return str;
    +
    79 }
    +
    80 
    +
    81 /**
    +
    82  * Remove heading white spaces of the string.
    +
    83  *
    +
    84  * @param str source string
    +
    85  *
    +
    86  * @return a pointer of source string if successful, otherwise returns NULL
    +
    87  *
    +
    88  * @note This modify source string directly.
    +
    89  */
    +
    90 char *qstrtrim_head(char *str) {
    +
    91  if (str == NULL)
    +
    92  return NULL;
    +
    93 
    +
    94  char *ss;
    +
    95  for (ss = str; *ss == ' ' || *ss == '\t' || *ss == '\r' || *ss == '\n';
    +
    96  ss++)
    +
    97  ;
    +
    98 
    +
    99  if (ss > str) {
    +
    100  size_t len = strlen(ss) + 1;
    +
    101  memmove(str, ss, len);
    +
    102  }
    +
    103 
    +
    104  return str;
    +
    105 }
    +
    106 
    +
    107 /**
    +
    108  * Remove tailing white spaces(including CR, LF) of the string.
    +
    109  *
    +
    110  * @param str source string
    +
    111  *
    +
    112  * @return a pointer of source string if successful, otherwise returns NULL
    +
    113  *
    +
    114  * @note This modify source string directly.
    +
    115  */
    +
    116 char *qstrtrim_tail(char *str) {
    +
    117  if (str == NULL)
    +
    118  return NULL;
    +
    119 
    +
    120  char *se;
    +
    121  for (se = str + strlen(str) - 1;
    +
    122  se >= str
    +
    123  && (*se == ' ' || *se == '\t' || *se == '\r' || *se == '\n');
    +
    124  se--)
    +
    125  ;
    +
    126  se++;
    +
    127  *se = '\0';
    +
    128 
    +
    129  return str;
    +
    130 }
    +
    131 
    +
    132 /**
    +
    133  * Remove character from head and tail of the string.
    +
    134  *
    +
    135  * @param str source string
    +
    136  * @param head heading character
    +
    137  * @param tail tailing character
    +
    138  *
    +
    139  * @return a pointer of source string if successful, otherwise returns NULL
    +
    140  *
    +
    141  * @note This modify source string directly.
    +
    142  *
    +
    143  * @code
    +
    144  * char *str = strdup(" \"hello world\" ");
    +
    145  * qstrtrim(str); // to remove white spaces
    +
    146  * qstrunchar(str, '"', '"'); // to unquote
    +
    147  * @endcode
    +
    148  */
    +
    149 char *qstrunchar(char *str, char head, char tail) {
    +
    150  if (str == NULL)
    +
    151  return NULL;
    +
    152 
    +
    153  int len = strlen(str);
    +
    154  if (len >= 2 && str[0] == head && str[len - 1] == tail) {
    +
    155  memmove(str, str + 1, len - 2);
    +
    156  str[len - 2] = '\0';
    +
    157  } else {
    +
    158  return NULL;
    +
    159  }
    +
    160 
    +
    161  return str;
    +
    162 }
    +
    163 
    +
    164 /**
    +
    165  * Replace string or tokens as word from source string with given mode.
    +
    166  *
    +
    167  * @param mode replacing mode
    +
    168  * @param srcstr source string
    +
    169  * @param tokstr token or string
    +
    170  * @param word target word to be replaced
    +
    171  *
    +
    172  * @return a pointer of malloced or source string depending on the mode if
    +
    173  * successful, otherwise returns NULL
    +
    174  *
    +
    175  * @note
    +
    176  * The mode argument has two separated characters. First character
    +
    177  * is used to decide replacing method and can be 't' or 's'.
    +
    178  * The character 't' and 's' stand on [t]oken and [s]tring.
    +
    179  *
    +
    180  * When 't' is given each character of the token string(third argument)
    +
    181  * will be compared with source string individually. If matched one
    +
    182  * is found. the character will be replaced with given work.
    +
    183  *
    +
    184  * If 's' is given instead of 't'. Token string will be analyzed
    +
    185  * only one chunk word. So the replacement will be occured when
    +
    186  * the case of whole word matched.
    +
    187  *
    +
    188  * Second character is used to decide returning memory type and
    +
    189  * can be 'n' or 'r' which are stand on [n]ew and [r]eplace.
    +
    190  *
    +
    191  * When 'n' is given the result will be placed into new array so
    +
    192  * you should free the return string after using. Instead of this,
    +
    193  * you can also use 'r' character to modify source string directly.
    +
    194  * In this case, given source string should have enough space. Be
    +
    195  * sure that untouchable value can not be used for source string.
    +
    196  *
    +
    197  * So there are four associatable modes such like below.
    +
    198  *
    +
    199  * Mode "tn" : [t]oken replacing & putting the result into [n]ew array.
    +
    200  * Mode "tr" : [t]oken replacing & [r]eplace source string directly.
    +
    201  * Mode "sn" : [s]tring replacing & putting the result into [n]ew array.
    +
    202  * Mode "sr" : [s]tring replacing & [r]eplace source string directly.
    +
    203  *
    +
    204  * @code
    +
    205  * char srcstr[256], *retstr;
    +
    206  * char mode[4][2+1] = {"tn", "tr", "sn", "sr"};
    +
    207  *
    +
    208  * for(i = 0; i < 4; i++) {
    +
    209  * strcpy(srcstr, "Welcome to The qDecoder Project.");
    +
    210  *
    +
    211  * printf("before %s : srcstr = %s\n", mode[i], srcstr);
    +
    212  * retstr = qstrreplace(mode[i], srcstr, "The", "_");
    +
    213  * printf("after %s : srcstr = %s\n", mode[i], srcstr);
    +
    214  * printf(" retstr = %s\n\n", retstr);
    +
    215  * if(mode[i][1] == 'n') free(retstr);
    +
    216  * }
    +
    217  *
    +
    218  * --[Result]--
    +
    219  * before tn : srcstr = Welcome to The qDecoder Project.
    +
    220  * after tn : srcstr = Welcome to The qDecoder Project.
    +
    221  * retstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    +
    222  *
    +
    223  * before tr : srcstr = Welcome to The qDecoder Project.
    +
    224  * after tr : srcstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    +
    225  * retstr = W_lcom_ _o ___ qD_cod_r Proj_c_.
    +
    226  *
    +
    227  * before sn : srcstr = Welcome to The qDecoder Project.
    +
    228  * after sn : srcstr = Welcome to The qDecoder Project.
    +
    229  * retstr = Welcome to _ qDecoder Project.
    +
    230  *
    +
    231  * before sr : srcstr = Welcome to The qDecoder Project.
    +
    232  * after sr : srcstr = Welcome to _ qDecoder Project.
    +
    233  * retstr = Welcome to _ qDecoder Project.
    +
    234  * @endcode
    +
    235  */
    +
    236 char *qstrreplace(const char *mode, char *srcstr, const char *tokstr,
    +
    237  const char *word) {
    +
    238  if (mode == NULL || strlen(mode) != 2|| srcstr == NULL || tokstr == NULL
    +
    239  || word == NULL) {
    +
    240  DEBUG("Unknown mode \"%s\".", mode);
    +
    241  return NULL;
    +
    242  }
    +
    243 
    +
    244  char *newstr, *newp, *srcp, *tokenp, *retp;
    +
    245  newstr = newp = srcp = tokenp = retp = NULL;
    +
    246 
    +
    247  char method = mode[0], memuse = mode[1];
    +
    248  int maxstrlen, tokstrlen;
    +
    249 
    +
    250  /* Put replaced string into malloced 'newstr' */
    +
    251  if (method == 't') { /* Token replace */
    +
    252  maxstrlen = strlen(srcstr) * ((strlen(word) > 0) ? strlen(word) : 1);
    +
    253  newstr = (char *) malloc(maxstrlen + 1);
    +
    254  if (newstr == NULL)
    +
    255  return NULL;
    +
    256 
    +
    257  for (srcp = (char *) srcstr, newp = newstr; *srcp; srcp++) {
    +
    258  for (tokenp = (char *) tokstr; *tokenp; tokenp++) {
    +
    259  if (*srcp == *tokenp) {
    +
    260  char *wordp;
    +
    261  for (wordp = (char *) word; *wordp; wordp++) {
    +
    262  *newp++ = *wordp;
    +
    263  }
    +
    264  break;
    +
    265  }
    +
    266  }
    +
    267  if (!*tokenp)
    +
    268  *newp++ = *srcp;
    +
    269  }
    +
    270  *newp = '\0';
    +
    271  } else if (method == 's') { /* String replace */
    +
    272  if (strlen(word) > strlen(tokstr)) {
    +
    273  maxstrlen = ((strlen(srcstr) / strlen(tokstr)) * strlen(word))
    +
    274  + (strlen(srcstr) % strlen(tokstr));
    +
    275  } else {
    +
    276  maxstrlen = strlen(srcstr);
    +
    277  }
    +
    278  newstr = (char *) malloc(maxstrlen + 1);
    +
    279  if (newstr == NULL)
    +
    280  return NULL;
    +
    281 
    +
    282  tokstrlen = strlen(tokstr);
    +
    283 
    +
    284  for (srcp = srcstr, newp = newstr; *srcp; srcp++) {
    +
    285  if (!strncmp(srcp, tokstr, tokstrlen)) {
    +
    286  char *wordp;
    +
    287  for (wordp = (char *) word; *wordp; wordp++)
    +
    288  *newp++ = *wordp;
    +
    289  srcp += tokstrlen - 1;
    +
    290  } else
    +
    291  *newp++ = *srcp;
    +
    292  }
    +
    293  *newp = '\0';
    +
    294  } else {
    +
    295  DEBUG("Unknown mode \"%s\".", mode);
    +
    296  return NULL;
    +
    297  }
    +
    298 
    +
    299  /* decide whether newing the memory or replacing into exist one */
    +
    300  if (memuse == 'n')
    +
    301  retp = newstr;
    +
    302  else if (memuse == 'r') {
    +
    303  strcpy(srcstr, newstr);
    +
    304  free(newstr);
    +
    305  retp = srcstr;
    +
    306  } else {
    +
    307  DEBUG("Unknown mode \"%s\".", mode);
    +
    308  free(newstr);
    +
    309  return NULL;
    +
    310  }
    +
    311 
    +
    312  return retp;
    +
    313 }
    +
    314 
    +
    315 /**
    +
    316  * Copy src string to dst. The dst string array will be always terminated by
    +
    317  * NULL character. Also allows overlap between src and dst.
    +
    318  *
    +
    319  * @param dst a pointer of the string to be copied
    +
    320  * @param size the size of dst character arrary
    +
    321  * @param src a pointer of source string
    +
    322  *
    +
    323  * @return always returns a pointer of dst
    +
    324  */
    +
    325 char *qstrcpy(char *dst, size_t size, const char *src) {
    +
    326  if (dst == NULL || size == 0 || src == NULL)
    +
    327  return dst;
    +
    328 
    +
    329  size_t nbytes = strlen(src);
    +
    330  return qstrncpy(dst, size, src, nbytes);
    +
    331 }
    +
    332 
    +
    333 /**
    +
    334  * Copy src string to dst no more than n bytes. The dst string array will be
    +
    335  * always terminated by NULL character. Also allows overlap between src and dst.
    +
    336  *
    +
    337  * @param dst a pointer of the string to be copied
    +
    338  * @param size the size of dst character arrary
    +
    339  * @param src a pointer of source string
    +
    340  * @param nbytes number of bytes to copy
    +
    341  *
    +
    342  * @return always returns a pointer of dst
    +
    343  */
    +
    344 char *qstrncpy(char *dst, size_t size, const char *src, size_t nbytes) {
    +
    345  if (dst == NULL || size == 0 || src == NULL)
    +
    346  return dst;
    +
    347 
    +
    348  if (nbytes >= size)
    +
    349  nbytes = size - 1;
    +
    350  memmove((void *) dst, (void *) src, nbytes);
    +
    351  dst[nbytes] = '\0';
    +
    352 
    +
    353  return dst;
    +
    354 }
    +
    355 
    +
    356 /**
    +
    357  * Duplicate a formatted string.
    +
    358  *
    +
    359  * @param format string format
    +
    360  *
    +
    361  * @return a pointer of malloced string if successful, otherwise returns NULL
    +
    362  */
    +
    363 char *qstrdupf(const char *format, ...) {
    +
    364  char *str;
    +
    365  DYNAMIC_VSPRINTF(str, format);
    +
    366  if (str == NULL)
    +
    367  return NULL;
    +
    368 
    +
    369  char *dup = strdup(str);
    +
    370  free(str);
    +
    371 
    +
    372  return dup;
    +
    373 }
    +
    374 
    +
    375 /**
    +
    376  * Duplicate a substing set
    +
    377  *
    +
    378  * @param str a pointer of original string
    +
    379  * @param start substring which is started with this
    +
    380  * @param end substring which is ended with this
    +
    381  *
    +
    382  * @return a pointer of malloced string if successful, otherwise returns NULL
    +
    383  */
    +
    384 char *qstrdup_between(const char *str, const char *start, const char *end) {
    +
    385  char *s;
    +
    386  if ((s = strstr(str, start)) == NULL)
    +
    387  return NULL;
    +
    388  s += strlen(start);
    +
    389 
    +
    390  char *e;
    +
    391  if ((e = strstr(s, end)) == NULL)
    +
    392  return NULL;
    +
    393 
    +
    394  int len = e - s;
    +
    395 
    +
    396  char *buf = (char *) malloc(sizeof(char) * (len + 1));
    +
    397  if (buf == NULL)
    +
    398  return NULL;
    +
    399 
    +
    400  strncpy(buf, s, len);
    +
    401  buf[len] = '\0';
    +
    402  return buf;
    +
    403 }
    +
    404 
    +
    405 /**
    +
    406  * Duplicate a copy of memory data.
    +
    407  *
    +
    408  * @param data source data
    +
    409  * @param size data size
    +
    410  *
    +
    411  * @return a pointer of malloced data which's content is identical to source data.
    +
    412  */
    +
    413 void *qmemdup(const void *data, size_t size) {
    +
    414  if (data == NULL || size == 0) {
    +
    415  return NULL;
    +
    416  }
    +
    417 
    +
    418  void *newdata = malloc(size);
    +
    419  if (newdata == NULL) {
    +
    420  return NULL;
    +
    421  }
    +
    422 
    +
    423  memcpy(newdata, data, size);
    +
    424  return newdata;
    +
    425 }
    +
    426 
    +
    427 /**
    +
    428  * Append formatted string to the end of the source str
    +
    429  *
    +
    430  * @param str a pointer of original string
    +
    431  * @param format string format to append
    +
    432  *
    +
    433  * @return a pointer of str if successful, otherwise returns NULL
    +
    434  */
    +
    435 char *qstrcatf(char *str, const char *format, ...) {
    +
    436  char *buf;
    +
    437  DYNAMIC_VSPRINTF(buf, format);
    +
    438  if (buf == NULL)
    +
    439  return NULL;
    +
    440 
    +
    441  char *ret = strcat(str, buf);
    +
    442  free(buf);
    +
    443  return ret;
    +
    444 }
    +
    445 
    +
    446 /**
    +
    447  * Get one line from the string offset.
    +
    448  *
    +
    449  * @param buf buffer pointer
    +
    450  * @param size buffer size
    +
    451  * @param offset a offset pointer which point source string
    +
    452  *
    +
    453  * @return a pointer of buffer if successful, otherwise(EOF) returns NULL
    +
    454  *
    +
    455  * @note
    +
    456  * CR and LF will not be stored.
    +
    457  *
    +
    458  * @code
    +
    459  * char *text="Hello\nWorld";
    +
    460  *
    +
    461  * char *offset = text;
    +
    462  * char buf[1024];
    +
    463  * while(qstrgets(buf, sizeof(buf), &offset) == NULL) {
    +
    464  * printf("%s\n", buf);
    +
    465  * }
    +
    466  * @endcode
    +
    467  */
    +
    468 char *qstrgets(char *buf, size_t size, char **offset) {
    +
    469  if (offset == NULL || *offset == NULL || **offset == '\0')
    +
    470  return NULL;
    +
    471 
    +
    472  size_t i;
    +
    473  char *from = *offset;
    +
    474  char *to = buf;
    +
    475  for (i = 0; *from != '\0' && i < (size - 1); i++, from++) {
    +
    476  if (*from == '\r')
    +
    477  continue;
    +
    478  if (*from == '\n') {
    +
    479  from++;
    +
    480  break;
    +
    481  }
    +
    482  *to = *from;
    +
    483  to++;
    +
    484  }
    +
    485  *to = '\0';
    +
    486  *offset = from;
    +
    487 
    +
    488  return buf;
    +
    489 }
    +
    490 
    +
    491 /**
    +
    492  * Reverse the order of characters in the string
    +
    493  *
    +
    494  * @param str a pointer of source string
    +
    495  *
    +
    496  * @return always returns a pointer of str
    +
    497  *
    +
    498  * @note This modify str directly.
    +
    499  */
    +
    500 char *qstrrev(char *str) {
    +
    501  if (str == NULL)
    +
    502  return str;
    +
    503 
    +
    504  char *p1, *p2;
    +
    505  for (p1 = str, p2 = str + (strlen(str) - 1); p2 > p1; p1++, p2--) {
    +
    506  char t = *p1;
    +
    507  *p1 = *p2;
    +
    508  *p2 = t;
    +
    509  }
    +
    510 
    +
    511  return str;
    +
    512 }
    +
    513 
    +
    514 /**
    +
    515  * Convert character to bigger character.
    +
    516  *
    +
    517  * @param str a pointer of source string
    +
    518  *
    +
    519  * @return always returns a pointer of str
    +
    520  *
    +
    521  * @note This modify str directly.
    +
    522  */
    +
    523 char *qstrupper(char *str) {
    +
    524  char *cp;
    +
    525 
    +
    526  if (!str)
    +
    527  return NULL;
    +
    528  for (cp = str; *cp; cp++)
    +
    529  if (*cp >= 'a' && *cp <= 'z')
    +
    530  *cp -= 32;
    +
    531  return str;
    +
    532 }
    +
    533 
    +
    534 /**
    +
    535  * Convert character to lower character.
    +
    536  *
    +
    537  * @param str a pointer of source string
    +
    538  *
    +
    539  * @return always returns a pointer of str
    +
    540  *
    +
    541  * @note This modify str directly.
    +
    542  */
    +
    543 char *qstrlower(char *str) {
    +
    544  char *cp;
    +
    545 
    +
    546  if (!str)
    +
    547  return NULL;
    +
    548  for (cp = str; *cp; cp++)
    +
    549  if (*cp >= 'A' && *cp <= 'Z')
    +
    550  *cp += 32;
    +
    551  return str;
    +
    552 }
    +
    553 
    +
    554 /**
    +
    555  * Split string into tokens
    +
    556  *
    +
    557  * @param str source string
    +
    558  * @param delimiters string that specifies a set of delimiters that may
    +
    559  * surround the token being extracted
    +
    560  * @param retstop stop delimiter character will be stored. it can be NULL
    +
    561  * if you don't want to know.
    +
    562  * @param offset integer pointer used for store last position.
    +
    563  * (must be reset to 0)
    +
    564  *
    +
    565  * @return a pointer to the first byte of a token if successful, otherwise
    +
    566  * returns NULL.
    +
    567  *
    +
    568  * @code
    +
    569  * char *str = strdup("Hello,world|Thank,you");
    +
    570  * char *token;
    +
    571  * int offset = 0;
    +
    572  * while((token = qstrtok(str, "|,", NULL, &offset)) != NULL) {
    +
    573  * printf("%s\n", token);
    +
    574  * }
    +
    575  * @endcode
    +
    576  *
    +
    577  * @note
    +
    578  * This may modify str argument.
    +
    579  * The major difference between qstrtok() and standard strtok() is that
    +
    580  * qstrtok() can returns empty string tokens. If the str is "a:b::d", qstrtok()
    +
    581  * returns "a", "b", "", "d". But strtok() returns "a","b","d".
    +
    582  */
    +
    583 char *qstrtok(char *str, const char *delimiters, char *retstop, int *offset) {
    +
    584  char *tokensp, *tokenep;
    +
    585 
    +
    586  tokensp = tokenep = (char *) (str + *offset);
    +
    587  int numdel = strlen(delimiters);
    +
    588  for (; *tokenep; tokenep++) {
    +
    589  int j;
    +
    590  for (j = 0; j < numdel; j++) {
    +
    591  if (*tokenep == delimiters[j]) {
    +
    592  if (retstop != NULL)
    +
    593  *retstop = delimiters[j];
    +
    594  *tokenep = '\0';
    +
    595  tokenep++;
    +
    596  *offset = tokenep - str;
    +
    597  return tokensp;
    +
    598  }
    +
    599  }
    +
    600  }
    +
    601 
    +
    602  if (retstop != NULL)
    +
    603  *retstop = '\0';
    +
    604  if (tokensp != tokenep) {
    +
    605  *offset = tokenep - str;
    +
    606  return tokensp;
    +
    607  }
    +
    608  return NULL;
    +
    609 }
    +
    610 
    +
    611 /**
    +
    612  * String Tokenizer
    +
    613  *
    +
    614  * @param str source string
    +
    615  * @param delimiters string that specifies a set of delimiters that may
    +
    616  * surround the token being extracted
    +
    617  *
    +
    618  * @return qlist container pointer otherwise returns NULL.
    +
    619  *
    +
    620  * @code
    +
    621  * qlist_t *tokens = qstr_tokenizer("a:b:c", ":");
    +
    622  * char *str;
    +
    623  * while((str = tokens->popfirst(tokens, NULL)) != NULL) {
    +
    624  * printf("%s\n", str);
    +
    625  * }
    +
    626  * tokens->free(tokens);
    +
    627  * @endcode
    +
    628  */
    +
    629 qlist_t *qstrtokenizer(const char *str, const char *delimiters) {
    +
    630  qlist_t *list = qlist(0);
    +
    631  if (list == NULL)
    +
    632  return NULL;
    +
    633 
    +
    634  int i;
    +
    635  char *dupstr = strdup(str);
    +
    636  char *token;
    +
    637  int offset = 0;
    +
    638  for (i = 1, token = qstrtok(dupstr, delimiters, NULL, &offset);
    +
    639  token != NULL;
    +
    640  token = qstrtok(dupstr, delimiters, NULL, &offset), i++) {
    +
    641  list->addlast(list, token, strlen(token) + 1);
    +
    642  }
    +
    643  free(dupstr);
    +
    644 
    +
    645  return list;
    +
    646 }
    +
    647 
    +
    648 /**
    +
    649  * Generate unique id
    +
    650  *
    +
    651  * @param seed additional seed string. this can be NULL
    +
    652  *
    +
    653  * @return a pointer of malloced string
    +
    654  *
    +
    655  * @note
    +
    656  * The length of returned string is 32+1 bytes long including terminating NULL
    +
    657  * character. It's a good idea to call srand() once before calling this because
    +
    658  * it uses rand().
    +
    659  */
    +
    660 char *qstrunique(const char *seed) {
    +
    661  long usec;
    +
    662 #ifdef _WIN32
    +
    663  FILETIME ft;
    +
    664  GetSystemTimeAsFileTime(&ft);
    +
    665  usec = ft.dwLowDateTime % 1000000;
    +
    666 #else
    +
    667  struct timeval tv;
    +
    668  gettimeofday(&tv, NULL);
    +
    669  usec = tv.tv_usec;
    +
    670 #endif
    +
    671 
    +
    672  char uniquestr[128];
    +
    673  snprintf(uniquestr, sizeof(uniquestr), "%u%d%lu%ld%s", getpid(), rand(),
    +
    674  (unsigned long)time(NULL), usec, (seed != NULL) ? seed : "");
    +
    675 
    +
    676  unsigned char md5hash[16];
    +
    677  qhashmd5(uniquestr, strlen(uniquestr), md5hash);
    +
    678  char *md5ascii = qhex_encode(md5hash, 16);
    +
    679 
    +
    680  return md5ascii;
    +
    681 }
    +
    682 
    +
    683 /**
    +
    684  * Convert integer to comma string.
    +
    685  *
    +
    686  * @param number integer
    +
    687  *
    +
    688  * @return a pointer of malloced string which contains comma separated number
    +
    689  * if successful, otherwise returns NULL
    +
    690  */
    +
    691 char *qstr_comma_number(int number) {
    +
    692  char *str, *strp;
    +
    693 
    +
    694  str = strp = (char *) malloc(sizeof(char) * (14 + 1));
    +
    695  if (str == NULL)
    +
    696  return NULL;
    +
    697 
    +
    698  char buf[10 + 1], *bufp;
    +
    699  snprintf(buf, sizeof(buf), "%d", abs(number));
    +
    700 
    +
    701  if (number < 0)
    +
    702  *strp++ = '-';
    +
    703  for (bufp = buf; *bufp != '\0'; strp++, bufp++) {
    +
    704  *strp = *bufp;
    +
    705  if ((strlen(bufp)) % 3 == 1 && *(bufp + 1) != '\0')
    +
    706  *(++strp) = ',';
    +
    707  }
    +
    708  *strp = '\0';
    +
    709 
    +
    710  return str;
    +
    711 }
    +
    712 
    +
    713 /**
    +
    714  * Test for an alpha-numeric string
    +
    715  *
    +
    716  * @param testfunc test function for individual character
    +
    717  * @param str a pointer of string
    +
    718  *
    +
    719  * @return true for ok, otherwise returns false
    +
    720  *
    +
    721  * @code
    +
    722  * if(qstrtest(isalnum, "hello1234") == true) {
    +
    723  * printf("It is alpha-numeric string.");
    +
    724  * }
    +
    725  *
    +
    726  * if(qstrtest(isdigit, "0123456789") == true) {
    +
    727  * printf("It is alpha-numeric string.");
    +
    728  * }
    +
    729  * @endcode
    +
    730  *
    +
    731  * @note
    +
    732  * Basically you can use below test functios without creating your own version.
    +
    733  * Make sure <ctype.h> header should be included before using any of these
    +
    734  * functions.
    +
    735  * isalnum - checks for an alphanumeric character.
    +
    736  * isalpha - checks for an alphabetic character.
    +
    737  * isascii - checks whether c is a 7-bit unsigned char value that fits into
    +
    738  * the ASCII character set.
    +
    739  * isblank - checks for a blank character; that is, a space or a tab.
    +
    740  * iscntrl - checks for a control character.
    +
    741  * isdigit - checks for a digit (0 through 9).
    +
    742  * isgraph - checks for any printable character except space.
    +
    743  * islower - checks for a lower-case character.
    +
    744  * isprint - checks for any printable character including space.
    +
    745  * ispunct - checks for any printable character which is not a space or an
    +
    746  * alphanumeric character.
    +
    747  * isspace - checks for white-space characters.
    +
    748  * isupper - checks for an uppercase letter.
    +
    749  * isxdigit - checks for a hexadecimal digits.
    +
    750  * Please refer "man isalnum" for more details about these functions.
    +
    751  */
    +
    752 bool qstrtest(int (*testfunc)(int), const char *str) {
    +
    753  for (; *str; str++) {
    +
    754  if (testfunc(*str) == 0)
    +
    755  return false;
    +
    756  }
    +
    757  return true;
    +
    758 }
    +
    759 
    +
    760 /**
    +
    761  * Test for an email-address formatted string
    +
    762  *
    +
    763  * @param email email-address formatted string
    +
    764  *
    +
    765  * @return true if successful, otherwise returns false
    +
    766  */
    +
    767 bool qstr_is_email(const char *email) {
    +
    768  int i, alpa, dot, gol;
    +
    769 
    +
    770  if (email == NULL)
    +
    771  return false;
    +
    772 
    +
    773  for (i = alpa = dot = gol = 0; email[i] != '\0'; i++) {
    +
    774  switch (email[i]) {
    +
    775  case '@': {
    +
    776  if (alpa == 0)
    +
    777  return false;
    +
    778  if (gol > 0)
    +
    779  return false;
    +
    780  gol++;
    +
    781  break;
    +
    782  }
    +
    783  case '.': {
    +
    784  if ((i > 0) && (email[i - 1] == '@'))
    +
    785  return false;
    +
    786  if ((gol > 0) && (email[i - 1] == '.'))
    +
    787  return false;
    +
    788  dot++;
    +
    789  break;
    +
    790  }
    +
    791  default: {
    +
    792  alpa++;
    +
    793  if ((email[i] >= '0') && (email[i] <= '9'))
    +
    794  break;
    +
    795  else if ((email[i] >= 'A') && (email[i] <= 'Z'))
    +
    796  break;
    +
    797  else if ((email[i] >= 'a') && (email[i] <= 'z'))
    +
    798  break;
    +
    799  else if ((email[i] == '-') || (email[i] == '_'))
    +
    800  break;
    +
    801  else
    +
    802  return false;
    +
    803  }
    +
    804  }
    +
    805  }
    +
    806 
    +
    807  if ((alpa <= 3) || (gol == 0) || (dot == 0))
    +
    808  return false;
    +
    809  return true;
    +
    810 }
    +
    811 
    +
    812 /**
    +
    813  * Test for an IPv4 address string
    +
    814  *
    +
    815  * @param url IPv4 address string
    +
    816  *
    +
    817  * @return true if successful, otherwise returns false
    +
    818  *
    +
    819  * @code
    +
    820  * if(qstr_is_ip4addr("1.2.3.4") == true) {
    +
    821  * printf("It is IPv4 address string.");
    +
    822  * }
    +
    823  * @endcode
    +
    824  */
    +
    825 bool qstr_is_ip4addr(const char *str) {
    +
    826  char *dupstr = strdup(str);
    +
    827 
    +
    828  char *s1, *s2;
    +
    829  int periodcnt;
    +
    830  for (s1 = dupstr, periodcnt = 0; (s2 = strchr(s1, '.')) != NULL;
    +
    831  s1 = s2 + 1, periodcnt++) {
    +
    832  *s2 = '\0';
    +
    833 
    +
    834  int n;
    +
    835  if (qstrtest(isdigit, s1) == false || (n = atoi(s1)) <= 0 || n >= 256) {
    +
    836  free(dupstr);
    +
    837  return false;
    +
    838  }
    +
    839  }
    +
    840 
    +
    841  free(dupstr);
    +
    842  if (periodcnt != 3)
    +
    843  return false;
    +
    844  return true;
    +
    845 }
    +
    846 
    +
    847 #ifdef __linux__
    +
    848 #include <iconv.h>
    +
    849 #endif
    +
    850 
    +
    851 /**
    +
    852  * Convert character encoding
    +
    853  *
    +
    854  * @param str additional seed string. this can be NULL
    +
    855  * @param fromcode encoding type of str
    +
    856  * @param tocode encoding to convert
    +
    857  * @param mag magnification between fromcode and tocode
    +
    858  *
    +
    859  * @return a pointer of malloced converted string if successful,
    +
    860  * otherwise returns NULL
    +
    861  *
    +
    862  * @code
    +
    863  * qCharEncode("KOREAN_EUCKR_STRING", "EUC-KR", "UTF-8", 1.5);
    +
    864  * @endcode
    +
    865  */
    +
    866 char *qstr_conv_encoding(const char *str, const char *fromcode,
    +
    867  const char *tocode, float mag) {
    +
    868 #ifdef __linux__
    +
    869  if (str == NULL)
    +
    870  return NULL;
    +
    871 
    +
    872  char *fromstr = (char *) str;
    +
    873  size_t fromsize = strlen(fromstr) + 1;
    +
    874 
    +
    875  size_t tosize = sizeof(char) * ((mag * (fromsize - 1)) + 1);
    +
    876  char *tostr = (char *) malloc(tosize);
    +
    877  if (tostr == NULL)
    +
    878  return NULL;
    +
    879 
    +
    880  char *tostr1 = tostr;
    +
    881 
    +
    882  iconv_t it = iconv_open(tocode, fromcode);
    +
    883  if (it < 0) {
    +
    884  DEBUG("iconv_open() failed.");
    +
    885  return NULL;
    +
    886  }
    +
    887 
    +
    888  int ret = iconv(it, &fromstr, &fromsize, &tostr, &tosize);
    +
    889  iconv_close(it);
    +
    890 
    +
    891  if (ret < 0) {
    +
    892  DEBUG("iconv() failed.");
    +
    893  free(tostr1);
    +
    894  return NULL;
    +
    895  }
    +
    896 
    +
    897  return tostr1;
    +
    898 #else
    +
    899  return NULL;
    +
    900 #endif
    +
    901 }
    +
    char * qhex_encode(const void *bin, size_t size)
    Encode data to Hexadecimal digit format.
    Definition: qencode.c:393
    +
    bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
    Calculate 128-bit(16-bytes) MD5 hash.
    Definition: qhash.c:67
    +
    static bool head(qhttpclient_t *client, const char *uri, int *rescode, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->head(): Sends a HEAD request.
    Definition: qhttpclient.c:558
    +
    qlist_t * qlist(int options)
    Create new qlist_t linked-list container.
    Definition: qlist.c:124
    +
    char * qstrreplace(const char *mode, char *srcstr, const char *tokstr, const char *word)
    Replace string or tokens as word from source string with given mode.
    Definition: qstring.c:236
    +
    char * qstrtrim_tail(char *str)
    Remove tailing white spaces(including CR, LF) of the string.
    Definition: qstring.c:116
    +
    char * qstrcatf(char *str, const char *format,...)
    Append formatted string to the end of the source str.
    Definition: qstring.c:435
    +
    char * qstrtok(char *str, const char *delimiters, char *retstop, int *offset)
    Split string into tokens.
    Definition: qstring.c:583
    +
    char * qstrdup_between(const char *str, const char *start, const char *end)
    Duplicate a substing set.
    Definition: qstring.c:384
    +
    bool qstrtest(int(*testfunc)(int), const char *str)
    Test for an alpha-numeric string.
    Definition: qstring.c:752
    +
    char * qstrtrim_head(char *str)
    Remove heading white spaces of the string.
    Definition: qstring.c:90
    +
    char * qstr_comma_number(int number)
    Convert integer to comma string.
    Definition: qstring.c:691
    +
    bool qstr_is_ip4addr(const char *str)
    Test for an IPv4 address string.
    Definition: qstring.c:825
    +
    char * qstrcpy(char *dst, size_t size, const char *src)
    Copy src string to dst.
    Definition: qstring.c:325
    +
    char * qstrupper(char *str)
    Convert character to bigger character.
    Definition: qstring.c:523
    +
    char * qstrunchar(char *str, char head, char tail)
    Remove character from head and tail of the string.
    Definition: qstring.c:149
    +
    char * qstrgets(char *buf, size_t size, char **offset)
    Get one line from the string offset.
    Definition: qstring.c:468
    +
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition: qstring.c:413
    +
    char * qstrrev(char *str)
    Reverse the order of characters in the string.
    Definition: qstring.c:500
    +
    char * qstrlower(char *str)
    Convert character to lower character.
    Definition: qstring.c:543
    +
    char * qstrunique(const char *seed)
    Generate unique id.
    Definition: qstring.c:660
    +
    char * qstr_conv_encoding(const char *str, const char *fromcode, const char *tocode, float mag)
    Convert character encoding.
    Definition: qstring.c:866
    +
    char * qstrncpy(char *dst, size_t size, const char *src, size_t nbytes)
    Copy src string to dst no more than n bytes.
    Definition: qstring.c:344
    +
    char * qstrtrim(char *str)
    Remove white spaces(including CR, LF) from head and tail of the string.
    Definition: qstring.c:55
    +
    qlist_t * qstrtokenizer(const char *str, const char *delimiters)
    String Tokenizer.
    Definition: qstring.c:629
    +
    bool qstr_is_email(const char *email)
    Test for an email-address formatted string.
    Definition: qstring.c:767
    +
    char * qstrdupf(const char *format,...)
    Duplicate a formatted string.
    Definition: qstring.c:363
    diff --git a/doc/html/qsystem_8c.html b/doc/html/qsystem_8c.html index 3aae5a6c..bd042f6d 100644 --- a/doc/html/qsystem_8c.html +++ b/doc/html/qsystem_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qsystem.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qsystem.c File Reference
    +
    +
    qsystem.c File Reference
    @@ -70,28 +70,28 @@

    Go to the source code of this file.

    - - - - - - - + + + + + +

    +

    Functions

    const char * qgetenv (const char *envname, const char *defstr)
     Get system environment variable.
     
    char * qsyscmd (const char *cmd)
     Get the result string of external command execution.
     
    const char * qgetenv (const char *envname, const char *defstr)
     Get system environment variable. More...
     
    char * qsyscmd (const char *cmd)
     Get the result string of external command execution. More...
     

    Detailed Description

    System APIs.

    Definition in file qsystem.c.

    Function Documentation

    - -

    ◆ qgetenv()

    + +

    ◆ qgetenv()

    - + @@ -124,14 +124,14 @@

    -

    ◆ qsyscmd()

    + +

    ◆ qsyscmd()

    const char * qgetenv const char* qgetenv ( const char *  envname,
    - + @@ -160,7 +160,7 @@

    diff --git a/doc/html/qsystem_8c.js b/doc/html/qsystem_8c.js index 311a0983..e63f5e34 100644 --- a/doc/html/qsystem_8c.js +++ b/doc/html/qsystem_8c.js @@ -1,5 +1,5 @@ var qsystem_8c = [ - [ "qgetenv", "qsystem_8c.html#a3913af5fdd1ab50b75601d112a965988", null ], - [ "qsyscmd", "qsystem_8c.html#a739f64aa9825fe23965b11d71b0674f5", null ] + [ "qgetenv", "qsystem_8c.html#a572fee0d88280a0dd1f5ab0905296eea", null ], + [ "qsyscmd", "qsystem_8c.html#a5463b8e0d69c2f317da6d9c47dee14f4", null ] ]; \ No newline at end of file diff --git a/doc/html/qsystem_8c_source.html b/doc/html/qsystem_8c_source.html index d9dea552..894bafd4 100644 --- a/doc/html/qsystem_8c_source.html +++ b/doc/html/qsystem_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qsystem.c Source File @@ -20,8 +20,8 @@

    char * qsyscmd char* qsyscmd ( const char *  cmd)
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,108 +52,109 @@
    -
    qsystem.c
    +
    +
    qsystem.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qsystem.c System APIs.
    -
    31 */
    -
    32
    -
    33#include <stdio.h>
    -
    34#include <stdlib.h>
    -
    35#include <stdbool.h>
    -
    36#include <string.h>
    -
    37#include <unistd.h>
    -
    38#include <netdb.h>
    -
    39#include <sys/socket.h>
    -
    40#include <netinet/in.h>
    -
    41#include <arpa/inet.h>
    -
    42#include "qinternal.h"
    -
    43#include "utilities/qfile.h"
    -
    44#include "utilities/qsystem.h"
    -
    45
    -
    46/**
    -
    47 * Get system environment variable
    -
    48 *
    -
    49 * @param envname environment name
    -
    50 * @param defstr if not found, return this string
    -
    51 *
    -
    52 * @return a pointer of environment variable
    -
    53 */
    -
    54const char *qgetenv(const char *envname, const char *defstr) {
    -
    55 const char *envstr = getenv(envname);
    -
    56 return (envstr) ? envstr : defstr;
    -
    57}
    -
    58
    -
    59/**
    -
    60 * Get the result string of external command execution
    -
    61 *
    -
    62 * @param cmd external command
    -
    63 *
    -
    64 * @return malloced string pointer which contains result if successful,
    -
    65 * otherwise returns NULL
    -
    66 *
    -
    67 * @note
    -
    68 * If the command does not report result but it is executed successfully,
    -
    69 * this will returns empty string(not null)
    -
    70 */
    -
    71char *qsyscmd(const char *cmd) {
    -
    72 FILE *fp = popen(cmd, "r");
    -
    73 if (fp == NULL)
    -
    74 return NULL;
    -
    75 char *str = qfile_read(fp, NULL);
    -
    76 pclose(fp);
    -
    77
    -
    78 if (str == NULL)
    -
    79 str = strdup("");
    -
    80 return str;
    -
    81}
    -
    82
    -
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition qfile.c:214
    -
    static void * cmd(qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.
    -
    const char * qgetenv(const char *envname, const char *defstr)
    Get system environment variable.
    Definition qsystem.c:54
    -
    char * qsyscmd(const char *cmd)
    Get the result string of external command execution.
    Definition qsystem.c:71
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qsystem.c System APIs.
    +
    31  */
    +
    32 
    +
    33 #include <stdio.h>
    +
    34 #include <stdlib.h>
    +
    35 #include <stdbool.h>
    +
    36 #include <string.h>
    +
    37 #include <unistd.h>
    +
    38 #include <netdb.h>
    +
    39 #include <sys/socket.h>
    +
    40 #include <netinet/in.h>
    +
    41 #include <arpa/inet.h>
    +
    42 #include "qinternal.h"
    +
    43 #include "utilities/qfile.h"
    +
    44 #include "utilities/qsystem.h"
    +
    45 
    +
    46 /**
    +
    47  * Get system environment variable
    +
    48  *
    +
    49  * @param envname environment name
    +
    50  * @param defstr if not found, return this string
    +
    51  *
    +
    52  * @return a pointer of environment variable
    +
    53  */
    +
    54 const char *qgetenv(const char *envname, const char *defstr) {
    +
    55  const char *envstr = getenv(envname);
    +
    56  return (envstr) ? envstr : defstr;
    +
    57 }
    +
    58 
    +
    59 /**
    +
    60  * Get the result string of external command execution
    +
    61  *
    +
    62  * @param cmd external command
    +
    63  *
    +
    64  * @return malloced string pointer which contains result if successful,
    +
    65  * otherwise returns NULL
    +
    66  *
    +
    67  * @note
    +
    68  * If the command does not report result but it is executed successfully,
    +
    69  * this will returns empty string(not null)
    +
    70  */
    +
    71 char *qsyscmd(const char *cmd) {
    +
    72  FILE *fp = popen(cmd, "r");
    +
    73  if (fp == NULL)
    +
    74  return NULL;
    +
    75  char *str = qfile_read(fp, NULL);
    +
    76  pclose(fp);
    +
    77 
    +
    78  if (str == NULL)
    +
    79  str = strdup("");
    +
    80  return str;
    +
    81 }
    +
    82 
    +
    void * qfile_read(FILE *fp, size_t *nbytes)
    Read data from a file stream.
    Definition: qfile.c:214
    +
    static void * cmd(qhttpclient_t *client, const char *method, const char *uri, void *data, size_t size, int *rescode, size_t *contentslength, qlisttbl_t *reqheaders, qlisttbl_t *resheaders)
    qhttpclient->cmd(): Sends a custom request(method) to the remote host and reads it's response.
    Definition: qhttpclient.c:1099
    +
    char * qsyscmd(const char *cmd)
    Get the result string of external command execution.
    Definition: qsystem.c:71
    +
    const char * qgetenv(const char *envname, const char *defstr)
    Get system environment variable.
    Definition: qsystem.c:54
    diff --git a/doc/html/qtime_8c.html b/doc/html/qtime_8c.html index f8c233db..9391b4b2 100644 --- a/doc/html/qtime_8c.html +++ b/doc/html/qtime_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qtime.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -62,7 +61,8 @@ -
    qtime.c File Reference
    +
    +
    qtime.c File Reference

    @@ -71,89 +71,40 @@

    Go to the source code of this file.

    - - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - +

    +

    Functions

    long qtime_current_milli (void)
     Returns the current time in milliseconds.
     Returns the current time in milliseconds. More...
     
    char * qtime_localtime_strf (char *buf, int size, time_t utctime, const char *format)
     Get custom formmatted local time string.
     
    char * qtime_localtime_str (time_t utctime)
     Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
     
    const char * qtime_localtime_staticstr (time_t utctime)
     Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
     
    char * qtime_gmt_strf (char *buf, int size, time_t utctime, const char *format)
     Get custom formmatted GMT time string.
     
    char * qtime_gmt_str (time_t utctime)
     Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
     
    const char * qtime_gmt_staticstr (time_t utctime)
     Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
     
    char * qtime_localtime_strf (char *buf, int size, time_t utctime, const char *format)
     Get custom formmatted local time string. More...
     
    char * qtime_localtime_str (time_t utctime)
     Get local time string formatted like '02-Nov-2007 16:37:39 +0900'. More...
     
    const char * qtime_localtime_staticstr (time_t utctime)
     Get local time string formatted like '02-Nov-2007 16:37:39 +0900'. More...
     
    char * qtime_gmt_strf (char *buf, int size, time_t utctime, const char *format)
     Get custom formmatted GMT time string. More...
     
    char * qtime_gmt_str (time_t utctime)
     Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'. More...
     
    const char * qtime_gmt_staticstr (time_t utctime)
     Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'. More...
     
    time_t qtime_parse_gmtstr (const char *gmtstr)
     This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT', 'Mon, 05 May 2008 03:50:39 +0900' and returns as universal time.
     This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT', 'Mon, 05 May 2008 03:50:39 +0900' and returns as universal time. More...
     

    Detailed Description

    Time handling APIs.

    Definition in file qtime.c.

    -

    Macro Definition Documentation

    - -

    ◆ __USE_XOPEN

    - -
    -
    - - - - -
    #define __USE_XOPEN
    -
    - -

    Definition at line 33 of file qtime.c.

    - -
    -
    - -

    ◆ _XOPEN_SOURCE

    - -
    -
    - - - - -
    #define _XOPEN_SOURCE
    -
    - -

    Definition at line 34 of file qtime.c.

    - -
    -
    - -

    ◆ _BSD_SOURCE

    - -
    -
    - - - - -
    #define _BSD_SOURCE
    -
    - -

    Definition at line 35 of file qtime.c.

    - -
    -
    -

    Function Documentation

    - -

    ◆ qtime_current_milli()

    +

    Function Documentation

    + +

    ◆ qtime_current_milli()

    @@ -175,14 +126,14 @@

    -

    ◆ qtime_localtime_strf()

    + +

    ◆ qtime_localtime_strf()

    - + @@ -224,24 +175,24 @@

    Returns
    string pointer of buf
    -
    char *timestr = qtime_localtime_strf(0, "%H:%M:%S"); // HH:MM:SS
    +
    char *timestr = qtime_localtime_strf(0, "%H:%M:%S"); // HH:MM:SS
    free(timestr);
    -
    char *timestr = qtime_localtime_strf(0, "%Y%m%d%H%M%S"); // YYMMDDhhmmss
    +
    char *timestr = qtime_localtime_strf(0, "%Y%m%d%H%M%S"); // YYMMDDhhmmss
    free(timestr);
    -
    char * qtime_localtime_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted local time string.
    Definition qtime.c:73
    +
    char * qtime_localtime_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted local time string.
    Definition: qtime.c:73

    Definition at line 73 of file qtime.c.

    - -

    ◆ qtime_localtime_str()

    + +

    ◆ qtime_localtime_str()

    char * qtime_localtime_strf char* qtime_localtime_strf ( char *  buf,
    - + @@ -259,26 +210,26 @@

    Returns
    mallocked string pointer of time string
    char *timestr;
    -
    +
    timestr = qtime_localtime_str(0); // now
    free(timestr);
    -
    timestr = qtime_localtime_str(time(NULL)); // same as above
    +
    timestr = qtime_localtime_str(time(NULL)); // same as above
    free(timestr);
    -
    timestr = qtime_localtime_str(time(NULL) - 86400)); // 1 day before
    +
    timestr = qtime_localtime_str(time(NULL) - 86400)); // 1 day before
    free(timestr);
    -
    char * qtime_localtime_str(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition qtime.c:103
    +
    char * qtime_localtime_str(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition: qtime.c:103

    Definition at line 103 of file qtime.c.

    - -

    ◆ qtime_localtime_staticstr()

    + +

    ◆ qtime_localtime_staticstr()

    char * qtime_localtime_str char* qtime_localtime_str ( time_t  utctime)
    - + @@ -295,22 +246,22 @@

    Returns
    internal static string pointer of time string
    -
    -
    printf("%s", qtime_localtime_staticstr(time(NULL) + 86400)); // 1 day later
    -
    const char * qtime_localtime_staticstr(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition qtime.c:125
    +
    printf("%s", qtime_localtime_staticstr(0)); // now
    +
    printf("%s", qtime_localtime_staticstr(time(NULL) + 86400)); // 1 day later
    +
    const char * qtime_localtime_staticstr(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition: qtime.c:125

    Definition at line 125 of file qtime.c.

    - -

    ◆ qtime_gmt_strf()

    + +

    ◆ qtime_gmt_strf()

    const char * qtime_localtime_staticstr const char* qtime_localtime_staticstr ( time_t  utctime)
    - + @@ -353,21 +304,21 @@

    Returns
    string pointer of buf
    char timestr[8+1];
    -
    qtime_gmt_strf(buf, sizeof(buf), 0, "%H:%M:%S"); // HH:MM:SS
    -
    char * qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted GMT time string.
    Definition qtime.c:148
    +
    qtime_gmt_strf(buf, sizeof(buf), 0, "%H:%M:%S"); // HH:MM:SS
    +
    char * qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted GMT time string.
    Definition: qtime.c:148

    Definition at line 148 of file qtime.c.

    - -

    ◆ qtime_gmt_str()

    + +

    ◆ qtime_gmt_str()

    char * qtime_gmt_strf char* qtime_gmt_strf ( char *  buf,
    - + @@ -385,26 +336,26 @@

    Returns
    malloced string pointer which points GMT time string.
    char *timestr;
    -
    +
    timestr = qtime_gmt_str(0); // now
    free(timestr);
    -
    timestr = qtime_gmt_str(time(NULL)); // same as above
    +
    timestr = qtime_gmt_str(time(NULL)); // same as above
    free(timestr);
    -
    timestr = qtime_gmt_str(time(NULL) - 86400)); // 1 day before
    +
    timestr = qtime_gmt_str(time(NULL) - 86400)); // 1 day before
    free(timestr);
    -
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition qtime.c:174
    +
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition: qtime.c:174

    Definition at line 174 of file qtime.c.

    - -

    ◆ qtime_gmt_staticstr()

    + +

    ◆ qtime_gmt_staticstr()

    char * qtime_gmt_str char* qtime_gmt_str ( time_t  utctime)
    - + @@ -421,16 +372,16 @@

    Returns
    internal static string pointer which points GMT time string.
    -
    -
    printf("%s", qtime_gmt_staticstr(time(NULL) + 86400)); // 1 day later
    -
    const char * qtime_gmt_staticstr(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition qtime.c:197
    +
    printf("%s", qtime_gmt_staticstr(0)); // now
    +
    printf("%s", qtime_gmt_staticstr(time(NULL) + 86400)); // 1 day later
    +
    const char * qtime_gmt_staticstr(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition: qtime.c:197

    Definition at line 197 of file qtime.c.

    - -

    ◆ qtime_parse_gmtstr()

    + +

    ◆ qtime_parse_gmtstr()

    @@ -454,12 +405,12 @@

    Returns
    universal time(UTC). in case of conversion error, returns -1.
    time_t t = time(NULL);
    -
    +
    char *s = qtime_parse_gmtstr(t);
    printf("%d\n", t);
    printf("%s\n", s);
    -
    printf("%d\n", qtime_parse_gmtstr(s)); // this must be same as t
    +
    printf("%d\n", qtime_parse_gmtstr(s)); // this must be same as t
    free(s);
    -
    time_t qtime_parse_gmtstr(const char *gmtstr)
    This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT',...
    Definition qtime.c:223
    +
    time_t qtime_parse_gmtstr(const char *gmtstr)
    This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT',...
    Definition: qtime.c:223

    Definition at line 223 of file qtime.c.

    @@ -471,7 +422,7 @@

    diff --git a/doc/html/qtime_8c.js b/doc/html/qtime_8c.js index a3bbe4f8..5aebc901 100644 --- a/doc/html/qtime_8c.js +++ b/doc/html/qtime_8c.js @@ -1,11 +1,14 @@ var qtime_8c = [ + [ "__USE_XOPEN", "qtime_8c.html#a8773045a81f883f2ab00761f45e8642c", null ], + [ "_XOPEN_SOURCE", "qtime_8c.html#a78c99ffd76a7bb3c8c74db76207e9ab4", null ], + [ "_BSD_SOURCE", "qtime_8c.html#ad3d8a3bd0c0b677acef144f2c2ef6d73", null ], [ "qtime_current_milli", "qtime_8c.html#a689f608f871f45fb772266bf1f18cdd7", null ], - [ "qtime_localtime_strf", "qtime_8c.html#a7f023ccf5f6120df54e4f1a9f034b9a9", null ], - [ "qtime_localtime_str", "qtime_8c.html#a6116a83810dfd2d593e236abb417bdcd", null ], - [ "qtime_localtime_staticstr", "qtime_8c.html#afb4e26399a9e779429aa0592e4ad03b6", null ], - [ "qtime_gmt_strf", "qtime_8c.html#a11790ec49dee0ad865ca7392790a6b8b", null ], - [ "qtime_gmt_str", "qtime_8c.html#a393447c2ed7764c85937ecdb3ed5a252", null ], - [ "qtime_gmt_staticstr", "qtime_8c.html#ac83d07a78536a71e3161c00156342677", null ], + [ "qtime_localtime_strf", "qtime_8c.html#a0feed44026ab24d9d366c5997a2173b4", null ], + [ "qtime_localtime_str", "qtime_8c.html#a89b28885c2bb13906855cd6c34cfaed7", null ], + [ "qtime_localtime_staticstr", "qtime_8c.html#adf628449c7e5425b5c16b61f14a51eaf", null ], + [ "qtime_gmt_strf", "qtime_8c.html#a1a3f24d676b909313177459cefa78899", null ], + [ "qtime_gmt_str", "qtime_8c.html#acbacded92c55a0674eec7590b65d7a3d", null ], + [ "qtime_gmt_staticstr", "qtime_8c.html#a7e54e9b38d82df0fe112440a89b372dc", null ], [ "qtime_parse_gmtstr", "qtime_8c.html#a6c536941cca900b390fc199eb3b50c6b", null ] ]; \ No newline at end of file diff --git a/doc/html/qtime_8c_source.html b/doc/html/qtime_8c_source.html index f42cfcf8..adbc4f17 100644 --- a/doc/html/qtime_8c_source.html +++ b/doc/html/qtime_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: utilities/qtime.c Source File @@ -20,8 +20,8 @@

    const char * qtime_gmt_staticstr const char* qtime_gmt_staticstr ( time_t  utctime)
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,272 +52,273 @@

    -
    qtime.c
    +
    +
    qtime.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qtime.c Time handling APIs.
    -
    31 */
    -
    32
    -
    33#define __USE_XOPEN
    -
    34#define _XOPEN_SOURCE
    -
    35#define _BSD_SOURCE
    -
    36#include <stdio.h>
    -
    37#include <stdlib.h>
    -
    38#include <stdbool.h>
    -
    39#include <string.h>
    -
    40#include <sys/time.h>
    -
    41#include "qinternal.h"
    -
    42#include "utilities/qtime.h"
    -
    43
    -
    44/**
    -
    45 * Returns the current time in milliseconds.
    -
    46 *
    -
    47 * @return current time in milliseconds.
    -
    48 */
    - -
    50 struct timeval tv;
    -
    51 gettimeofday(&tv, NULL);
    -
    52 long time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    -
    53 return time;
    -
    54}
    -
    55
    -
    56/**
    -
    57 * Get custom formmatted local time string.
    -
    58 *
    -
    59 * @param buf save buffer
    -
    60 * @param size buffer size
    -
    61 * @param utctime 0 for current time, universal time for specific time
    -
    62 * @param format format for strftime()
    -
    63 *
    -
    64 * @return string pointer of buf
    -
    65 *
    -
    66 * @code
    -
    67 * char *timestr = qtime_localtime_strf(0, "%H:%M:%S"); // HH:MM:SS
    -
    68 * free(timestr);
    -
    69 * char *timestr = qtime_localtime_strf(0, "%Y%m%d%H%M%S"); // YYMMDDhhmmss
    -
    70 * free(timestr);
    -
    71 * @endcode
    -
    72 */
    -
    73char *qtime_localtime_strf(char *buf, int size, time_t utctime,
    -
    74 const char *format) {
    -
    75 if (utctime == 0)
    -
    76 utctime = time(NULL);
    -
    77 struct tm *localtm = localtime(&utctime);
    -
    78
    -
    79 if (strftime(buf, size, format, localtm) == 0) {
    -
    80 snprintf(buf, size, "(buffer small)");
    -
    81 }
    -
    82
    -
    83 return buf;
    -
    84}
    -
    85
    -
    86/**
    -
    87 * Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    -
    88 *
    -
    89 * @param utctime 0 for current time, universal time for specific time
    -
    90 *
    -
    91 * @return mallocked string pointer of time string
    -
    92 *
    -
    93 * @code
    -
    94 * char *timestr;
    -
    95 * timestr = qtime_localtime_str(0); // now
    -
    96 * free(timestr);
    -
    97 * timestr = qtime_localtime_str(time(NULL)); // same as above
    -
    98 * free(timestr);
    -
    99 * timestr = qtime_localtime_str(time(NULL) - 86400)); // 1 day before
    -
    100 * free(timestr);
    -
    101 * @endcode
    -
    102 */
    -
    103char *qtime_localtime_str(time_t utctime) {
    -
    104 int size = sizeof(char) * (CONST_STRLEN("00-Jan-0000 00:00:00 +0000") + 1);
    -
    105 char *timestr = (char *) malloc(size);
    -
    106 if (timestr == NULL)
    -
    107 return NULL;
    -
    108
    -
    109 qtime_localtime_strf(timestr, size, utctime, "%d-%b-%Y %H:%M:%S %z");
    -
    110 return timestr;
    -
    111}
    -
    112
    -
    113/**
    -
    114 * Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    -
    115 *
    -
    116 * @param utctime 0 for current time, universal time for specific time
    -
    117 *
    -
    118 * @return internal static string pointer of time string
    -
    119 *
    -
    120 * @code
    -
    121 * printf("%s", qtime_localtime_staticstr(0)); // now
    -
    122 * printf("%s", qtime_localtime_staticstr(time(NULL) + 86400)); // 1 day later
    -
    123 * @endcode
    -
    124 */
    -
    125const char *qtime_localtime_staticstr(time_t utctime) {
    -
    126 static char timestr[sizeof(char)
    -
    127 * (CONST_STRLEN("00-Jan-0000 00:00:00 +0000") + 1)];
    -
    128 qtime_localtime_strf(timestr, sizeof(timestr), utctime,
    -
    129 "%d-%b-%Y %H:%M:%S %z");
    -
    130 return timestr;
    -
    131}
    -
    132
    -
    133/**
    -
    134 * Get custom formmatted GMT time string.
    -
    135 *
    -
    136 * @param buf save buffer
    -
    137 * @param size buffer size
    -
    138 * @param utctime 0 for current time, universal time for specific time
    -
    139 * @param format format for strftime()
    -
    140 *
    -
    141 * @return string pointer of buf
    -
    142 *
    -
    143 * @code
    -
    144 * char timestr[8+1];
    -
    145 * qtime_gmt_strf(buf, sizeof(buf), 0, "%H:%M:%S"); // HH:MM:SS
    -
    146 * @endcode
    -
    147 */
    -
    148char *qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format) {
    -
    149 if (utctime == 0)
    -
    150 utctime = time(NULL);
    -
    151 struct tm *gmtm = gmtime(&utctime);
    -
    152
    -
    153 strftime(buf, size, format, gmtm);
    -
    154 return buf;
    -
    155}
    -
    156
    -
    157/**
    -
    158 * Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    -
    159 *
    -
    160 * @param utctime 0 for current time, universal time for specific time
    -
    161 *
    -
    162 * @return malloced string pointer which points GMT time string.
    -
    163 *
    -
    164 * @code
    -
    165 * char *timestr;
    -
    166 * timestr = qtime_gmt_str(0); // now
    -
    167 * free(timestr);
    -
    168 * timestr = qtime_gmt_str(time(NULL)); // same as above
    -
    169 * free(timestr);
    -
    170 * timestr = qtime_gmt_str(time(NULL) - 86400)); // 1 day before
    -
    171 * free(timestr);
    -
    172 * @endcode
    -
    173 */
    -
    174char *qtime_gmt_str(time_t utctime) {
    -
    175 int size = sizeof(char)
    -
    176 * (CONST_STRLEN("Mon, 00 Jan 0000 00:00:00 GMT") + 1);
    -
    177 char *timestr = (char *) malloc(size);
    -
    178 if (timestr == NULL)
    -
    179 return NULL;
    -
    180
    -
    181 qtime_gmt_strf(timestr, size, utctime, "%a, %d %b %Y %H:%M:%S GMT");
    -
    182 return timestr;
    -
    183}
    -
    184
    -
    185/**
    -
    186 * Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    -
    187 *
    -
    188 * @param utctime 0 for current time, universal time for specific time
    -
    189 *
    -
    190 * @return internal static string pointer which points GMT time string.
    -
    191 *
    -
    192 * @code
    -
    193 * printf("%s", qtime_gmt_staticstr(0)); // now
    -
    194 * printf("%s", qtime_gmt_staticstr(time(NULL) + 86400)); // 1 day later
    -
    195 * @endcode
    -
    196 */
    -
    197const char *qtime_gmt_staticstr(time_t utctime) {
    -
    198 static char timestr[sizeof(char)
    -
    199 * (CONST_STRLEN("Mon, 00-Jan-0000 00:00:00 GMT") + 1)];
    -
    200 qtime_gmt_strf(timestr, sizeof(timestr), utctime,
    -
    201 "%a, %d %b %Y %H:%M:%S GMT");
    -
    202 return timestr;
    -
    203}
    -
    204
    -
    205/**
    -
    206 * This parses GMT/Timezone(+/-) formatted time sting like
    -
    207 * 'Sun, 04 May 2008 18:50:39 GMT', 'Mon, 05 May 2008 03:50:39 +0900'
    -
    208 * and returns as universal time.
    -
    209 *
    -
    210 * @param gmtstr GMT/Timezone(+/-) formatted time string
    -
    211 *
    -
    212 * @return universal time(UTC). in case of conversion error, returns -1.
    -
    213 *
    -
    214 * @code
    -
    215 * time_t t = time(NULL);
    -
    216 * char *s = qtime_parse_gmtstr(t);
    -
    217 * printf("%d\n", t);
    -
    218 * printf("%s\n", s);
    -
    219 * printf("%d\n", qtime_parse_gmtstr(s)); // this must be same as t
    -
    220 * free(s);
    -
    221 * @endcode
    -
    222 */
    -
    223time_t qtime_parse_gmtstr(const char *gmtstr) {
    -
    224 struct tm gmtm;
    -
    225 if (strptime(gmtstr, "%a, %d %b %Y %H:%M:%S", &gmtm) == NULL)
    -
    226 return 0;
    -
    227 time_t utc = timegm(&gmtm);
    -
    228 if (utc < 0)
    -
    229 return -1;
    -
    230
    -
    231// parse timezone
    -
    232 char *p;
    -
    233 if ((p = strstr(gmtstr, "+")) != NULL) {
    -
    234 utc -= ((atoi(p + 1) / 100) * 60 * 60);
    -
    235 if (utc < 0)
    -
    236 return -1;
    -
    237 } else if ((p = strstr(gmtstr, "-")) != NULL) {
    -
    238 utc += ((atoi(p + 1) / 100) * 60 * 60);
    -
    239 }
    -
    240
    -
    241 return utc;
    -
    242}
    -
    char * qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted GMT time string.
    Definition qtime.c:148
    -
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition qtime.c:174
    -
    char * qtime_localtime_str(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition qtime.c:103
    -
    long qtime_current_milli(void)
    Returns the current time in milliseconds.
    Definition qtime.c:49
    -
    time_t qtime_parse_gmtstr(const char *gmtstr)
    This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT',...
    Definition qtime.c:223
    -
    char * qtime_localtime_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted local time string.
    Definition qtime.c:73
    -
    const char * qtime_gmt_staticstr(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition qtime.c:197
    -
    const char * qtime_localtime_staticstr(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition qtime.c:125
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qtime.c Time handling APIs.
    +
    31  */
    +
    32 
    +
    33 #define __USE_XOPEN
    +
    34 #define _XOPEN_SOURCE
    +
    35 #define _BSD_SOURCE
    +
    36 #include <stdio.h>
    +
    37 #include <stdlib.h>
    +
    38 #include <stdbool.h>
    +
    39 #include <string.h>
    +
    40 #include <sys/time.h>
    +
    41 #include "qinternal.h"
    +
    42 #include "utilities/qtime.h"
    +
    43 
    +
    44 /**
    +
    45  * Returns the current time in milliseconds.
    +
    46  *
    +
    47  * @return current time in milliseconds.
    +
    48  */
    +
    49 long qtime_current_milli(void) {
    +
    50  struct timeval tv;
    +
    51  gettimeofday(&tv, NULL);
    +
    52  long time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
    +
    53  return time;
    +
    54 }
    +
    55 
    +
    56 /**
    +
    57  * Get custom formmatted local time string.
    +
    58  *
    +
    59  * @param buf save buffer
    +
    60  * @param size buffer size
    +
    61  * @param utctime 0 for current time, universal time for specific time
    +
    62  * @param format format for strftime()
    +
    63  *
    +
    64  * @return string pointer of buf
    +
    65  *
    +
    66  * @code
    +
    67  * char *timestr = qtime_localtime_strf(0, "%H:%M:%S"); // HH:MM:SS
    +
    68  * free(timestr);
    +
    69  * char *timestr = qtime_localtime_strf(0, "%Y%m%d%H%M%S"); // YYMMDDhhmmss
    +
    70  * free(timestr);
    +
    71  * @endcode
    +
    72  */
    +
    73 char *qtime_localtime_strf(char *buf, int size, time_t utctime,
    +
    74  const char *format) {
    +
    75  if (utctime == 0)
    +
    76  utctime = time(NULL);
    +
    77  struct tm *localtm = localtime(&utctime);
    +
    78 
    +
    79  if (strftime(buf, size, format, localtm) == 0) {
    +
    80  snprintf(buf, size, "(buffer small)");
    +
    81  }
    +
    82 
    +
    83  return buf;
    +
    84 }
    +
    85 
    +
    86 /**
    +
    87  * Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    +
    88  *
    +
    89  * @param utctime 0 for current time, universal time for specific time
    +
    90  *
    +
    91  * @return mallocked string pointer of time string
    +
    92  *
    +
    93  * @code
    +
    94  * char *timestr;
    +
    95  * timestr = qtime_localtime_str(0); // now
    +
    96  * free(timestr);
    +
    97  * timestr = qtime_localtime_str(time(NULL)); // same as above
    +
    98  * free(timestr);
    +
    99  * timestr = qtime_localtime_str(time(NULL) - 86400)); // 1 day before
    +
    100  * free(timestr);
    +
    101  * @endcode
    +
    102  */
    +
    103 char *qtime_localtime_str(time_t utctime) {
    +
    104  int size = sizeof(char) * (CONST_STRLEN("00-Jan-0000 00:00:00 +0000") + 1);
    +
    105  char *timestr = (char *) malloc(size);
    +
    106  if (timestr == NULL)
    +
    107  return NULL;
    +
    108 
    +
    109  qtime_localtime_strf(timestr, size, utctime, "%d-%b-%Y %H:%M:%S %z");
    +
    110  return timestr;
    +
    111 }
    +
    112 
    +
    113 /**
    +
    114  * Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    +
    115  *
    +
    116  * @param utctime 0 for current time, universal time for specific time
    +
    117  *
    +
    118  * @return internal static string pointer of time string
    +
    119  *
    +
    120  * @code
    +
    121  * printf("%s", qtime_localtime_staticstr(0)); // now
    +
    122  * printf("%s", qtime_localtime_staticstr(time(NULL) + 86400)); // 1 day later
    +
    123  * @endcode
    +
    124  */
    +
    125 const char *qtime_localtime_staticstr(time_t utctime) {
    +
    126  static char timestr[sizeof(char)
    +
    127  * (CONST_STRLEN("00-Jan-0000 00:00:00 +0000") + 1)];
    +
    128  qtime_localtime_strf(timestr, sizeof(timestr), utctime,
    +
    129  "%d-%b-%Y %H:%M:%S %z");
    +
    130  return timestr;
    +
    131 }
    +
    132 
    +
    133 /**
    +
    134  * Get custom formmatted GMT time string.
    +
    135  *
    +
    136  * @param buf save buffer
    +
    137  * @param size buffer size
    +
    138  * @param utctime 0 for current time, universal time for specific time
    +
    139  * @param format format for strftime()
    +
    140  *
    +
    141  * @return string pointer of buf
    +
    142  *
    +
    143  * @code
    +
    144  * char timestr[8+1];
    +
    145  * qtime_gmt_strf(buf, sizeof(buf), 0, "%H:%M:%S"); // HH:MM:SS
    +
    146  * @endcode
    +
    147  */
    +
    148 char *qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format) {
    +
    149  if (utctime == 0)
    +
    150  utctime = time(NULL);
    +
    151  struct tm *gmtm = gmtime(&utctime);
    +
    152 
    +
    153  strftime(buf, size, format, gmtm);
    +
    154  return buf;
    +
    155 }
    +
    156 
    +
    157 /**
    +
    158  * Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    +
    159  *
    +
    160  * @param utctime 0 for current time, universal time for specific time
    +
    161  *
    +
    162  * @return malloced string pointer which points GMT time string.
    +
    163  *
    +
    164  * @code
    +
    165  * char *timestr;
    +
    166  * timestr = qtime_gmt_str(0); // now
    +
    167  * free(timestr);
    +
    168  * timestr = qtime_gmt_str(time(NULL)); // same as above
    +
    169  * free(timestr);
    +
    170  * timestr = qtime_gmt_str(time(NULL) - 86400)); // 1 day before
    +
    171  * free(timestr);
    +
    172  * @endcode
    +
    173  */
    +
    174 char *qtime_gmt_str(time_t utctime) {
    +
    175  int size = sizeof(char)
    +
    176  * (CONST_STRLEN("Mon, 00 Jan 0000 00:00:00 GMT") + 1);
    +
    177  char *timestr = (char *) malloc(size);
    +
    178  if (timestr == NULL)
    +
    179  return NULL;
    +
    180 
    +
    181  qtime_gmt_strf(timestr, size, utctime, "%a, %d %b %Y %H:%M:%S GMT");
    +
    182  return timestr;
    +
    183 }
    +
    184 
    +
    185 /**
    +
    186  * Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    +
    187  *
    +
    188  * @param utctime 0 for current time, universal time for specific time
    +
    189  *
    +
    190  * @return internal static string pointer which points GMT time string.
    +
    191  *
    +
    192  * @code
    +
    193  * printf("%s", qtime_gmt_staticstr(0)); // now
    +
    194  * printf("%s", qtime_gmt_staticstr(time(NULL) + 86400)); // 1 day later
    +
    195  * @endcode
    +
    196  */
    +
    197 const char *qtime_gmt_staticstr(time_t utctime) {
    +
    198  static char timestr[sizeof(char)
    +
    199  * (CONST_STRLEN("Mon, 00-Jan-0000 00:00:00 GMT") + 1)];
    +
    200  qtime_gmt_strf(timestr, sizeof(timestr), utctime,
    +
    201  "%a, %d %b %Y %H:%M:%S GMT");
    +
    202  return timestr;
    +
    203 }
    +
    204 
    +
    205 /**
    +
    206  * This parses GMT/Timezone(+/-) formatted time sting like
    +
    207  * 'Sun, 04 May 2008 18:50:39 GMT', 'Mon, 05 May 2008 03:50:39 +0900'
    +
    208  * and returns as universal time.
    +
    209  *
    +
    210  * @param gmtstr GMT/Timezone(+/-) formatted time string
    +
    211  *
    +
    212  * @return universal time(UTC). in case of conversion error, returns -1.
    +
    213  *
    +
    214  * @code
    +
    215  * time_t t = time(NULL);
    +
    216  * char *s = qtime_parse_gmtstr(t);
    +
    217  * printf("%d\n", t);
    +
    218  * printf("%s\n", s);
    +
    219  * printf("%d\n", qtime_parse_gmtstr(s)); // this must be same as t
    +
    220  * free(s);
    +
    221  * @endcode
    +
    222  */
    +
    223 time_t qtime_parse_gmtstr(const char *gmtstr) {
    +
    224  struct tm gmtm;
    +
    225  if (strptime(gmtstr, "%a, %d %b %Y %H:%M:%S", &gmtm) == NULL)
    +
    226  return 0;
    +
    227  time_t utc = timegm(&gmtm);
    +
    228  if (utc < 0)
    +
    229  return -1;
    +
    230 
    +
    231 // parse timezone
    +
    232  char *p;
    +
    233  if ((p = strstr(gmtstr, "+")) != NULL) {
    +
    234  utc -= ((atoi(p + 1) / 100) * 60 * 60);
    +
    235  if (utc < 0)
    +
    236  return -1;
    +
    237  } else if ((p = strstr(gmtstr, "-")) != NULL) {
    +
    238  utc += ((atoi(p + 1) / 100) * 60 * 60);
    +
    239  }
    +
    240 
    +
    241  return utc;
    +
    242 }
    +
    char * qtime_localtime_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted local time string.
    Definition: qtime.c:73
    +
    char * qtime_gmt_strf(char *buf, int size, time_t utctime, const char *format)
    Get custom formmatted GMT time string.
    Definition: qtime.c:148
    +
    long qtime_current_milli(void)
    Returns the current time in milliseconds.
    Definition: qtime.c:49
    +
    time_t qtime_parse_gmtstr(const char *gmtstr)
    This parses GMT/Timezone(+/-) formatted time sting like 'Sun, 04 May 2008 18:50:39 GMT',...
    Definition: qtime.c:223
    +
    const char * qtime_gmt_staticstr(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition: qtime.c:197
    +
    char * qtime_localtime_str(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition: qtime.c:103
    +
    char * qtime_gmt_str(time_t utctime)
    Get GMT time string formatted like 'Wed, 11-Nov-2007 23:19:25 GMT'.
    Definition: qtime.c:174
    +
    const char * qtime_localtime_staticstr(time_t utctime)
    Get local time string formatted like '02-Nov-2007 16:37:39 +0900'.
    Definition: qtime.c:125
    diff --git a/doc/html/qtokenbucket_8c.html b/doc/html/qtokenbucket_8c.html index c8a03f97..36b1c28f 100644 --- a/doc/html/qtokenbucket_8c.html +++ b/doc/html/qtokenbucket_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qtokenbucket.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qtokenbucket.c File Reference
    +
    +
    qtokenbucket.c File Reference
    @@ -70,16 +70,16 @@

    Go to the source code of this file.

    - - + - + - +

    +

    Functions

    void qtokenbucket_init (qtokenbucket_t *bucket, int init_tokens, int max_tokens, int tokens_per_sec)
     Initialize the token bucket.
     Initialize the token bucket. More...
     
    bool qtokenbucket_consume (qtokenbucket_t *bucket, int tokens)
     Consume tokens from the bucket.
     Consume tokens from the bucket. More...
     
    long qtokenbucket_waittime (qtokenbucket_t *bucket, int tokens)
     Get the estimate time until given number of token is ready.
     Get the estimate time until given number of token is ready. More...
     

    Detailed Description

    @@ -87,24 +87,24 @@

    Current implementation is not thread-safe.

    More information about token-bucket: http://en.wikipedia.org/wiki/Token_bucket

    qtokenbucket_t bucket;
    -
    qtokenbucket_init(&bucket, 500, 1000, 1000);
    +
    qtokenbucket_init(&bucket, 500, 1000, 1000);
    while (1) {
    -
    if (qtokenbucket_consume(&bucket, 1) == false) {
    +
    if (qtokenbucket_consume(&bucket, 1) == false) {
    // Bucket is empty. Let's wait
    -
    usleep(qtokenbucket_waittime(&bucket, 1) * 1000);
    +
    usleep(qtokenbucket_waittime(&bucket, 1) * 1000);
    continue;
    }
    // Got a token. Let's do something here.
    do_something();
    }
    -
    long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens)
    Get the estimate time until given number of token is ready.
    -
    void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens, int tokens_per_sec)
    Initialize the token bucket.
    -
    bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens)
    Consume tokens from the bucket.
    +
    long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens)
    Get the estimate time until given number of token is ready.
    Definition: qtokenbucket.c:109
    +
    void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens, int tokens_per_sec)
    Initialize the token bucket.
    Definition: qtokenbucket.c:76
    +
    bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens)
    Consume tokens from the bucket.
    Definition: qtokenbucket.c:93

    Definition in file qtokenbucket.c.

    Function Documentation

    - -

    ◆ qtokenbucket_init()

    + +

    ◆ qtokenbucket_init()

    @@ -155,8 +155,8 @@

    -

    ◆ qtokenbucket_consume()

    + +

    ◆ qtokenbucket_consume()

    @@ -195,8 +195,8 @@

    -

    ◆ qtokenbucket_waittime()

    + +

    ◆ qtokenbucket_waittime()

    @@ -240,7 +240,7 @@

    diff --git a/doc/html/qtokenbucket_8c_source.html b/doc/html/qtokenbucket_8c_source.html index 56ba19af..fc21d9b3 100644 --- a/doc/html/qtokenbucket_8c_source.html +++ b/doc/html/qtokenbucket_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: extensions/qtokenbucket.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,161 +52,162 @@

    -
    qtokenbucket.c
    +
    +
    qtokenbucket.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qtokenbucket.c Token Bucket implementation.
    -
    31 *
    -
    32 * Current implementation is not thread-safe.
    -
    33 *
    -
    34 * More information about token-bucket:
    -
    35 * http://en.wikipedia.org/wiki/Token_bucket
    -
    36 *
    -
    37 * @code
    -
    38 * qtokenbucket_t bucket;
    -
    39 * qtokenbucket_init(&bucket, 500, 1000, 1000);
    -
    40 * while (1) {
    -
    41 * if (qtokenbucket_consume(&bucket, 1) == false) {
    -
    42 * // Bucket is empty. Let's wait
    -
    43 * usleep(qtokenbucket_waittime(&bucket, 1) * 1000);
    -
    44 * continue;
    -
    45 * }
    -
    46 * // Got a token. Let's do something here.
    -
    47 * do_something();
    -
    48 * }
    -
    49 * @endcode
    -
    50 */
    -
    51
    -
    52#include "extensions/qtokenbucket.h"
    -
    53
    -
    54#include <stdio.h>
    -
    55#include <stdbool.h>
    -
    56#include <string.h>
    -
    57#include <sys/time.h>
    -
    58#include "utilities/qtime.h"
    -
    59#include "qinternal.h"
    -
    60
    -
    61#ifndef _DOXYGEN_SKIP
    -
    62/* internal functions */
    -
    63static void refill_tokens(qtokenbucket_t *bucket);
    -
    64#endif
    -
    65
    -
    66/**
    -
    67 * Initialize the token bucket.
    -
    68 *
    -
    69 * @param init_tokens
    -
    70 * the initial number of tokens.
    -
    71 * @param max_tokens
    -
    72 * maximum number of tokens in the bucket.
    -
    73 * @param tokens_per_sec
    -
    74 * number of tokens to fill per a second.
    -
    75 */
    -
    76void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens,
    -
    77 int tokens_per_sec) {
    -
    78 memset(bucket, 0, sizeof(qtokenbucket_t));
    -
    79 bucket->tokens = init_tokens;
    -
    80 bucket->max_tokens = max_tokens;
    -
    81 bucket->tokens_per_sec = tokens_per_sec;
    -
    82 bucket->last_fill = qtime_current_milli();
    -
    83}
    -
    84
    -
    85/**
    -
    86 * Consume tokens from the bucket.
    -
    87 *
    -
    88 * @param bucket tockenbucket object.
    -
    89 * @param tokens number of tokens to request.
    -
    90 *
    -
    91 * @return return true if there are enough tokens, otherwise false.
    -
    92 */
    -
    93bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens) {
    -
    94 refill_tokens(bucket);
    -
    95 if (bucket->tokens < tokens) {
    -
    96 return false;
    -
    97 }
    -
    98 bucket->tokens -= tokens;
    -
    99 return true;
    -
    100}
    -
    101
    -
    102/**
    -
    103 * Get the estimate time until given number of token is ready.
    -
    104 *
    -
    105 * @param tokens number of tokens
    -
    106 *
    -
    107 * @return estimated milliseconds
    -
    108 */
    -
    109long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens) {
    -
    110 refill_tokens(bucket);
    -
    111 if (bucket->tokens >= tokens) {
    -
    112 return 0;
    -
    113 }
    -
    114 int tokens_needed = tokens - (int)bucket->tokens;
    -
    115 double estimate_milli = (1000 * tokens_needed) / bucket->tokens_per_sec;
    -
    116 estimate_milli += ((1000 * tokens_needed) % bucket->tokens_per_sec) ? 1 : 0;
    -
    117 return estimate_milli;
    -
    118}
    -
    119
    -
    120#ifndef _DOXYGEN_SKIP
    -
    121/**
    -
    122 * Refill tokens.
    -
    123 */
    -
    124static void refill_tokens(qtokenbucket_t *bucket) {
    -
    125 long now = qtime_current_milli();
    -
    126 if (bucket->tokens < bucket->max_tokens) {
    -
    127 double new_tokens = (now - bucket->last_fill) * 0.001
    -
    128 * bucket->tokens_per_sec;
    -
    129 bucket->tokens =
    -
    130 ((bucket->tokens + new_tokens) < bucket->max_tokens) ?
    -
    131 (bucket->tokens + new_tokens) : bucket->max_tokens;
    -
    132 }
    -
    133 bucket->last_fill = now;
    -
    134}
    -
    135#endif // _DOXYGEN_SKIP
    -
    long qtime_current_milli(void)
    Returns the current time in milliseconds.
    Definition qtime.c:49
    -
    long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens)
    Get the estimate time until given number of token is ready.
    -
    void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens, int tokens_per_sec)
    Initialize the token bucket.
    -
    bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens)
    Consume tokens from the bucket.
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qtokenbucket.c Token Bucket implementation.
    +
    31  *
    +
    32  * Current implementation is not thread-safe.
    +
    33  *
    +
    34  * More information about token-bucket:
    +
    35  * http://en.wikipedia.org/wiki/Token_bucket
    +
    36  *
    +
    37  * @code
    +
    38  * qtokenbucket_t bucket;
    +
    39  * qtokenbucket_init(&bucket, 500, 1000, 1000);
    +
    40  * while (1) {
    +
    41  * if (qtokenbucket_consume(&bucket, 1) == false) {
    +
    42  * // Bucket is empty. Let's wait
    +
    43  * usleep(qtokenbucket_waittime(&bucket, 1) * 1000);
    +
    44  * continue;
    +
    45  * }
    +
    46  * // Got a token. Let's do something here.
    +
    47  * do_something();
    +
    48  * }
    +
    49  * @endcode
    +
    50  */
    +
    51 
    +
    52 #include "extensions/qtokenbucket.h"
    +
    53 
    +
    54 #include <stdio.h>
    +
    55 #include <stdbool.h>
    +
    56 #include <string.h>
    +
    57 #include <sys/time.h>
    +
    58 #include "utilities/qtime.h"
    +
    59 #include "qinternal.h"
    +
    60 
    +
    61 #ifndef _DOXYGEN_SKIP
    +
    62 /* internal functions */
    +
    63 static void refill_tokens(qtokenbucket_t *bucket);
    +
    64 #endif
    +
    65 
    +
    66 /**
    +
    67  * Initialize the token bucket.
    +
    68  *
    +
    69  * @param init_tokens
    +
    70  * the initial number of tokens.
    +
    71  * @param max_tokens
    +
    72  * maximum number of tokens in the bucket.
    +
    73  * @param tokens_per_sec
    +
    74  * number of tokens to fill per a second.
    +
    75  */
    +
    76 void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens,
    +
    77  int tokens_per_sec) {
    +
    78  memset(bucket, 0, sizeof(qtokenbucket_t));
    +
    79  bucket->tokens = init_tokens;
    +
    80  bucket->max_tokens = max_tokens;
    +
    81  bucket->tokens_per_sec = tokens_per_sec;
    +
    82  bucket->last_fill = qtime_current_milli();
    +
    83 }
    +
    84 
    +
    85 /**
    +
    86  * Consume tokens from the bucket.
    +
    87  *
    +
    88  * @param bucket tockenbucket object.
    +
    89  * @param tokens number of tokens to request.
    +
    90  *
    +
    91  * @return return true if there are enough tokens, otherwise false.
    +
    92  */
    +
    93 bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens) {
    +
    94  refill_tokens(bucket);
    +
    95  if (bucket->tokens < tokens) {
    +
    96  return false;
    +
    97  }
    +
    98  bucket->tokens -= tokens;
    +
    99  return true;
    +
    100 }
    +
    101 
    +
    102 /**
    +
    103  * Get the estimate time until given number of token is ready.
    +
    104  *
    +
    105  * @param tokens number of tokens
    +
    106  *
    +
    107  * @return estimated milliseconds
    +
    108  */
    +
    109 long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens) {
    +
    110  refill_tokens(bucket);
    +
    111  if (bucket->tokens >= tokens) {
    +
    112  return 0;
    +
    113  }
    +
    114  int tokens_needed = tokens - (int)bucket->tokens;
    +
    115  double estimate_milli = (1000 * tokens_needed) / bucket->tokens_per_sec;
    +
    116  estimate_milli += ((1000 * tokens_needed) % bucket->tokens_per_sec) ? 1 : 0;
    +
    117  return estimate_milli;
    +
    118 }
    +
    119 
    +
    120 #ifndef _DOXYGEN_SKIP
    +
    121 /**
    +
    122  * Refill tokens.
    +
    123  */
    +
    124 static void refill_tokens(qtokenbucket_t *bucket) {
    +
    125  long now = qtime_current_milli();
    +
    126  if (bucket->tokens < bucket->max_tokens) {
    +
    127  double new_tokens = (now - bucket->last_fill) * 0.001
    +
    128  * bucket->tokens_per_sec;
    +
    129  bucket->tokens =
    +
    130  ((bucket->tokens + new_tokens) < bucket->max_tokens) ?
    +
    131  (bucket->tokens + new_tokens) : bucket->max_tokens;
    +
    132  }
    +
    133  bucket->last_fill = now;
    +
    134 }
    +
    135 #endif // _DOXYGEN_SKIP
    +
    long qtime_current_milli(void)
    Returns the current time in milliseconds.
    Definition: qtime.c:49
    +
    long qtokenbucket_waittime(qtokenbucket_t *bucket, int tokens)
    Get the estimate time until given number of token is ready.
    Definition: qtokenbucket.c:109
    +
    void qtokenbucket_init(qtokenbucket_t *bucket, int init_tokens, int max_tokens, int tokens_per_sec)
    Initialize the token bucket.
    Definition: qtokenbucket.c:76
    +
    bool qtokenbucket_consume(qtokenbucket_t *bucket, int tokens)
    Consume tokens from the bucket.
    Definition: qtokenbucket.c:93
    diff --git a/doc/html/qtreetbl_8c.html b/doc/html/qtreetbl_8c.html index 262ce216..0c4f953f 100644 --- a/doc/html/qtreetbl_8c.html +++ b/doc/html/qtreetbl_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qtreetbl.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qtreetbl.c File Reference
    +
    +
    qtreetbl.c File Reference
    @@ -70,87 +70,88 @@

    Go to the source code of this file.

    - - - - + + + - + - + - + - + - + - - - - - - - - - + + + + + + + + + - + - + - + - - - - - - + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - +

    +

    Functions

    qtreetbl_t * qtreetbl (int options)
     Initialize a tree table.
     
    qtreetbl_t * qtreetbl (int options)
     Initialize a tree table. More...
     
    void qtreetbl_set_compare (qtreetbl_t *tbl, int(*cmp)(const void *name1, size_t namesize1, const void *name2, size_t namesize2))
     qtreetbl->set_compare(): Set user comparator.
     qtreetbl->set_compare(): Set user comparator. More...
     
    bool qtreetbl_put (qtreetbl_t *tbl, const char *name, const void *data, size_t datasize)
     qtreetbl->put(): Put an object into this table with string type key.
     qtreetbl->put(): Put an object into this table with string type key. More...
     
    bool qtreetbl_putstr (qtreetbl_t *tbl, const char *name, const char *str)
     qtreetbl->putstr(): Put a string into this table.
     qtreetbl->putstr(): Put a string into this table. More...
     
    bool qtreetbl_putstrf (qtreetbl_t *tbl, const char *name, const char *format,...)
     qtreetbl->putstrf(): Put a formatted string into this table.
     qtreetbl->putstrf(): Put a formatted string into this table. More...
     
    bool qtreetbl_putobj (qtreetbl_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
     qtreetbl->putobj(): Put an object data into this table with an object key.
     qtreetbl->putobj(): Put an object data into this table with an object key. More...
     
    void * qtreetbl_get (qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
     qtreetbl->get(): Get an object from this table.
     
    char * qtreetbl_getstr (qtreetbl_t *tbl, const char *name, const bool newmem)
     qtreetbl->getstr(): Finds an object and returns it as string type.
     
    void * qtreetbl_getobj (qtreetbl_t *tbl, const void *name, size_t namesize, size_t *datasize, bool newmem)
     qtreetbl->getobj(): Get an object from this table with an object name.
     
    void * qtreetbl_get (qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
     qtreetbl->get(): Get an object from this table. More...
     
    char * qtreetbl_getstr (qtreetbl_t *tbl, const char *name, const bool newmem)
     qtreetbl->getstr(): Finds an object and returns it as string type. More...
     
    void * qtreetbl_getobj (qtreetbl_t *tbl, const void *name, size_t namesize, size_t *datasize, bool newmem)
     qtreetbl->getobj(): Get an object from this table with an object name. More...
     
    bool qtreetbl_remove (qtreetbl_t *tbl, const char *name)
     qtreetbl->remove(): Remove an object from this table.
     qtreetbl->remove(): Remove an object from this table. More...
     
    bool qtreetbl_removeobj (qtreetbl_t *tbl, const void *name, size_t namesize)
     qtreetbl->remove(): Remove an object from this table with an object name.
     qtreetbl->remove(): Remove an object from this table with an object name. More...
     
    bool qtreetbl_getnext (qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem)
     qtreetbl->getnext(): Get next element.
     qtreetbl->getnext(): Get next element. More...
     
    void * qtreetbl_find_min (qtreetbl_t *tbl, size_t *namesize)
     qtreetbl->find_min(): Find the name of the leftmost object.
     
    void * qtreetbl_find_max (qtreetbl_t *tbl, size_t *namesize)
     qtreetbl->find_max(): Find the name of the rightmost object.
     
    void * qtreetbl_find_min (qtreetbl_t *tbl, size_t *namesize)
     qtreetbl->find_min(): Find the name of the leftmost object. More...
     
    void * qtreetbl_find_max (qtreetbl_t *tbl, size_t *namesize)
     qtreetbl->find_max(): Find the name of the rightmost object. More...
     
    qtreetbl_obj_t qtreetbl_find_nearest (qtreetbl_t *tbl, const void *name, size_t namesize, bool newmem)
     qtreetbl->find_nearest(): Find an object with matching key or nearest.
     qtreetbl->find_nearest(): Find an object with matching key or nearest. More...
     
    size_t qtreetbl_size (qtreetbl_t *tbl)
     qtreetbl->size(): Returns the number of keys in the table.
     qtreetbl->size(): Returns the number of keys in the table. More...
     
    void qtreetbl_clear (qtreetbl_t *tbl)
     qtreetbl->clear(): Clears the table so that it contains no keys.
     qtreetbl->clear(): Clears the table so that it contains no keys. More...
     
    void qtreetbl_lock (qtreetbl_t *tbl)
     qtreetbl->lock(): Enter critical section.
     qtreetbl->lock(): Enter critical section. More...
     
    void qtreetbl_unlock (qtreetbl_t *tbl)
     qtreetbl->unlock(): Leave critical section.
     qtreetbl->unlock(): Leave critical section. More...
     
    void qtreetbl_free (qtreetbl_t *tbl)
     qtreetbl->free(): De-allocate the table.
     qtreetbl->free(): De-allocate the table. More...
     
    int qtreetbl_byte_cmp (const void *name1, size_t namesize1, const void *name2, size_t namesize2)
    +int qtreetbl_byte_cmp (const void *name1, size_t namesize1, const void *name2, size_t namesize2)
     
    bool qtreetbl_debug (qtreetbl_t *tbl, FILE *out)
     qtreetbl->debug(): Print the internal tree structure in text.
     qtreetbl->debug(): Print the internal tree structure in text. More...
     
    int node_check_root (qtreetbl_t *tbl)
     Verifies that root property of the red-black tree is satisfied.
     Verifies that root property of the red-black tree is satisfied. More...
     
    int node_check_red (qtreetbl_t *tbl, qtreetbl_obj_t *obj)
     Verifies that red property of the red-black tree is satisfied.
     Verifies that red property of the red-black tree is satisfied. More...
     
    int node_check_black (qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len)
     Verifies that black property of the red-black tree is satisfied.
     Verifies that black property of the red-black tree is satisfied. More...
     
    int node_check_llrb (qtreetbl_t *tbl, qtreetbl_obj_t *obj)
     Verifies that LLRB property of the left-leaning red-black tree is satisfied.
     Verifies that LLRB property of the left-leaning red-black tree is satisfied. More...
     
    int qtreetbl_check (qtreetbl_t *tbl)
     Verifies that the invariants of the red-black tree are satisfied.
     Verifies that the invariants of the red-black tree are satisfied. More...
     

    Detailed Description

    @@ -186,7 +187,7 @@
  • iteration from given key.
  • find min/max key.
  • -
    qtreetbl_t *tbl = qtreetbl(QTREETBL_THREADSAFE);
    +
    qtreetbl_t *tbl = qtreetbl(QTREETBL_THREADSAFE);
    tbl->put(tbl, "KEY", "DATA", 4); // use putobj() for binary keys.
    void *data = tbl->get(tbl, "KEY", false); // use getobj() for binary keys.
    @@ -218,18 +219,18 @@
    size_t num = tbl->size(tbl);
    tbl->free(tbl);
    -
    qtreetbl_t * qtreetbl(int options)
    Initialize a tree table.
    Definition qtreetbl.c:182
    +
    qtreetbl_t * qtreetbl(int options)
    Initialize a tree table.
    Definition: qtreetbl.c:182

    Definition in file qtreetbl.c.

    Function Documentation

    - -

    ◆ qtreetbl()

    + +

    ◆ qtreetbl()

    - + @@ -255,7 +256,7 @@

    +
    qtreetbl_t *tbl = qtreetbl(0);
    Note
    Available options:
    • QTREETBL_THREADSAFE - make it thread-safe.
    @@ -265,8 +266,8 @@

    -

    ◆ qtreetbl_set_compare()

    + +

    ◆ qtreetbl_set_compare()

    @@ -305,8 +306,8 @@

    -

    ◆ qtreetbl_put()

    + +

    ◆ qtreetbl_put()

    @@ -369,8 +370,8 @@

    -

    ◆ qtreetbl_putstr()

    + +

    ◆ qtreetbl_putstr()

    @@ -426,8 +427,8 @@

    -

    ◆ qtreetbl_putstrf()

    + +

    ◆ qtreetbl_putstrf()

    @@ -489,8 +490,8 @@

    -

    ◆ qtreetbl_putobj()

    + +

    ◆ qtreetbl_putobj()

    @@ -561,14 +562,14 @@

    -

    ◆ qtreetbl_get()

    + +

    ◆ qtreetbl_get()

    qtreetbl_t * qtreetbl qtreetbl_t* qtreetbl ( int  options)
    - + @@ -621,7 +622,7 @@

    +
    qtreetbl_t *tbl = qtreetbl(0);
    (...codes...)
    // with newmem flag unset
    @@ -638,14 +639,14 @@

    -

    ◆ qtreetbl_getstr()

    + +

    ◆ qtreetbl_getstr()

    void * qtreetbl_get void* qtreetbl_get ( qtreetbl_t *  tbl,
    - + @@ -697,14 +698,14 @@

    -

    ◆ qtreetbl_getobj()

    + +

    ◆ qtreetbl_getobj()

    char * qtreetbl_getstr char* qtreetbl_getstr ( qtreetbl_t *  tbl,
    - + @@ -770,8 +771,8 @@

    -

    ◆ qtreetbl_remove()

    + +

    ◆ qtreetbl_remove()

    @@ -820,8 +821,8 @@

    -

    ◆ qtreetbl_removeobj()

    + +

    ◆ qtreetbl_removeobj()

    @@ -877,8 +878,8 @@

    -

    ◆ qtreetbl_getnext()

    + +

    ◆ qtreetbl_getnext()

    @@ -961,7 +962,7 @@

    tbl->lock(tbl);

    while (tbl->getnext(tbl, &obj, false) == true) {
    if (...condition...) {
    -
    char *name = qmemdup(obj.name, obj.namesize); // keep the name
    +
    char *name = qmemdup(obj.name, obj.namesize); // keep the name
    size_t namesize = obj.namesize; // for removal argument
    tbl->removeobj(tbl, obj.name, obj.namesize); // remove
    obj = tbl->find_nearest(tbl, name, namesize, false); // rewind one step back
    @@ -969,7 +970,7 @@

    }

    }
    tbl->unlock(tbl);
    -
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition qstring.c:413
    +
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition: qstring.c:413

    Note
    • Data insertion or deletion can be made during the traversal, but in that case iterator doesn't guarantee full sweep and possibly skip some visits. When deletion happens in getnext() loop, use find_nearest() to rewind the iterator one step back.
    • Object obj should be initialized with 0 by using memset() before first call.
    • @@ -981,14 +982,14 @@

      -

      ◆ qtreetbl_find_min()

      + +

      ◆ qtreetbl_find_min()

    void * qtreetbl_getobj void* qtreetbl_getobj ( qtreetbl_t *  tbl,
    - + @@ -1022,14 +1023,14 @@

    -

    ◆ qtreetbl_find_max()

    + +

    ◆ qtreetbl_find_max()

    void * qtreetbl_find_min void* qtreetbl_find_min ( qtreetbl_t *  tbl,
    - + @@ -1063,8 +1064,8 @@

    -

    ◆ qtreetbl_find_nearest()

    + +

    ◆ qtreetbl_find_nearest()

    @@ -1134,8 +1135,8 @@

    -

    ◆ qtreetbl_size()

    + +

    ◆ qtreetbl_size()

    @@ -1163,8 +1164,8 @@

    -

    ◆ qtreetbl_clear()

    + +

    ◆ qtreetbl_clear()

    @@ -1191,8 +1192,8 @@

    -

    ◆ qtreetbl_lock()

    + +

    ◆ qtreetbl_lock()

    @@ -1222,8 +1223,8 @@

    -

    ◆ qtreetbl_unlock()

    + +

    ◆ qtreetbl_unlock()

    @@ -1251,8 +1252,8 @@

    -

    ◆ qtreetbl_free()

    + +

    ◆ qtreetbl_free()

    @@ -1279,50 +1280,8 @@

    -

    ◆ qtreetbl_byte_cmp()

    - -
    -
    -

    void * qtreetbl_find_max void* qtreetbl_find_max ( qtreetbl_t *  tbl,
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    int qtreetbl_byte_cmp (const void * name1,
    size_t namesize1,
    const void * name2,
    size_t namesize2 
    )
    -
    - -

    Definition at line 833 of file qtreetbl.c.

    - -
    -
    - -

    ◆ qtreetbl_debug()

    + +

    ◆ qtreetbl_debug()

    @@ -1383,8 +1342,8 @@

    -

    ◆ node_check_root()

    + +

    ◆ node_check_root()

    @@ -1412,8 +1371,8 @@

    -

    ◆ node_check_red()

    + +

    ◆ node_check_red()

    @@ -1452,8 +1411,8 @@

    -

    ◆ node_check_black()

    + +

    ◆ node_check_black()

    @@ -1499,8 +1458,8 @@

    -

    ◆ node_check_llrb()

    + +

    ◆ node_check_llrb()

    @@ -1539,8 +1498,8 @@

    -

    ◆ qtreetbl_check()

    + +

    ◆ qtreetbl_check()

    @@ -1574,7 +1533,7 @@

    diff --git a/doc/html/qtreetbl_8c.js b/doc/html/qtreetbl_8c.js index a3bbd458..f6ef05d9 100644 --- a/doc/html/qtreetbl_8c.js +++ b/doc/html/qtreetbl_8c.js @@ -1,25 +1,26 @@ var qtreetbl_8c = [ - [ "qtreetbl", "qtreetbl_8c.html#a0a5c039995463a94bb7066190a6653e1", null ], + [ "qtreetbl", "qtreetbl_8c.html#a598c177098cb87a5d2f9e898cc2cfeed", null ], [ "qtreetbl_set_compare", "qtreetbl_8c.html#af9e239f384bd9939c34361a45dd496a6", null ], [ "qtreetbl_put", "qtreetbl_8c.html#a49e05111c8541c7155e352fb618cf170", null ], [ "qtreetbl_putstr", "qtreetbl_8c.html#a31e5908f6885033a58e354928e489b83", null ], [ "qtreetbl_putstrf", "qtreetbl_8c.html#ac475383244c35814d0950f0fa0c6414e", null ], [ "qtreetbl_putobj", "qtreetbl_8c.html#a74b90269930ae1607271c187514ac5e5", null ], - [ "qtreetbl_get", "qtreetbl_8c.html#a106198f0800d8892e404e0020da0d685", null ], - [ "qtreetbl_getstr", "qtreetbl_8c.html#a80185e2ca955ce2ccbdb43065110df4c", null ], - [ "qtreetbl_getobj", "qtreetbl_8c.html#a756340d5313f0ecf604c35326b3b6a5f", null ], + [ "qtreetbl_get", "qtreetbl_8c.html#ab3232b711f0462533a066ecffb01a297", null ], + [ "qtreetbl_getstr", "qtreetbl_8c.html#a54b587a2c2d1a377cdc54c0e2e32d400", null ], + [ "qtreetbl_getobj", "qtreetbl_8c.html#a7782fbe2dc1aa9f1be267c67e5ab3e37", null ], [ "qtreetbl_remove", "qtreetbl_8c.html#a3354408a36cb5d6fd724727dda04d1f3", null ], [ "qtreetbl_removeobj", "qtreetbl_8c.html#ad3284afb03b64b8242687e33b87aaf0e", null ], [ "qtreetbl_getnext", "qtreetbl_8c.html#aeb32bf5a193a0b7276cff302d3fcd090", null ], - [ "qtreetbl_find_min", "qtreetbl_8c.html#ac12b7b164b262053427eac34a2d1cbbc", null ], - [ "qtreetbl_find_max", "qtreetbl_8c.html#a590374e22090d003406210704b1c91d4", null ], + [ "qtreetbl_find_min", "qtreetbl_8c.html#a1f037a026ec877becd4f010226e00ea4", null ], + [ "qtreetbl_find_max", "qtreetbl_8c.html#a1f70611ea89775d0ec580cc13eaa0069", null ], [ "qtreetbl_find_nearest", "qtreetbl_8c.html#aa2c4f3ac4623feb251fadabbdf734bdd", null ], [ "qtreetbl_size", "qtreetbl_8c.html#a3537212a86616d50459eec229d94f8e4", null ], [ "qtreetbl_clear", "qtreetbl_8c.html#a7b324cf9e317a2623c5767b8a0183f72", null ], [ "qtreetbl_lock", "qtreetbl_8c.html#a5c8aa458bac0dc5e7dc0ab97c6f52dfd", null ], [ "qtreetbl_unlock", "qtreetbl_8c.html#a88a8e67f478390c6b25780fda3134a8d", null ], [ "qtreetbl_free", "qtreetbl_8c.html#a2bac240b706b85273338a26fb986eee7", null ], + [ "qtreetbl_byte_cmp", "qtreetbl_8c.html#a61a3463749adb906e549d51f8a9c5a51", null ], [ "qtreetbl_debug", "qtreetbl_8c.html#a663dfe23addb8debd1c3da5e54d2c930", null ], [ "node_check_root", "qtreetbl_8c.html#a27db959def75174ccdab3fd073e8b8fc", null ], [ "node_check_red", "qtreetbl_8c.html#acca3f6925989955eec665c5c9aa11ed2", null ], diff --git a/doc/html/qtreetbl_8c_source.html b/doc/html/qtreetbl_8c_source.html index fbf03d1c..e478e2dc 100644 --- a/doc/html/qtreetbl_8c_source.html +++ b/doc/html/qtreetbl_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qtreetbl.c Source File @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1407 +52,1408 @@

    -
    qtreetbl.c
    +
    +
    qtreetbl.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2023 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28
    -
    29/**
    -
    30 * @file qtreetbl.c Tree Table container that implements the Left-Leaning
    -
    31 * Red-Black BST algorithm.
    -
    32 *
    -
    33 * qtreetbl implements a binary search tree that allows efficient in-order
    -
    34 * traversal with O(log n) search time.
    -
    35 *
    -
    36 * The algorithm qtreetbl specifically implements is the left-leaning red-black
    -
    37 * tree algorithm invented in 2008 by Robert Sedgewick. A left-leaning red–black
    -
    38 * tree is a type of self-balancing binary search tree and it is a variant of the
    -
    39 * red–black tree which was invented in 1972 by Rudolf Bayer.
    -
    40 *
    -
    41 * References:
    -
    42 * - http://en.wikipedia.org/wiki/Left-leaning_red-black_tree
    -
    43 * - https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf
    -
    44 *
    -
    45 * (E)
    -
    46 * ______________|______________
    -
    47 * / \
    -
    48 * (C) (R)
    -
    49 * / \ / \
    -
    50 * (A,B) (D) (I,N) (S,X)
    -
    51 *
    -
    52 * <2-3-4 Tree Data Structure>
    -
    53 *
    -
    54 * E
    -
    55 * ______________|______________
    -
    56 * / \
    -
    57 * C R
    -
    58 * _______|_______ _______|_______
    -
    59 * / \ / \
    -
    60 * B D N X
    -
    61 * // // //
    -
    62 * A* I* S*
    -
    63 *
    -
    64 * <Left-Leaning Red-Black Tree Data Structure>
    -
    65 * Nodes A, I and S are nodes with RED upper link. Others are BLACK
    -
    66 *
    -
    67 *
    -
    68 * The Red-Black BST algorithm has been one of the popular BST algorithms
    -
    69 * especially for in-memory operation. The Left-Leaning version of Red-Black
    -
    70 * especially improves performance and reduces overall complexity.
    -
    71 *
    -
    72 * Since it's relatively new algorithm, there's not many practically functional
    -
    73 * working codes yet other than proof of concept kinds. Here's one of fully
    -
    74 * functional codes and I, Seungyoung Kim, would like to dedicate this code to
    -
    75 * the genius inventor Robert Sedgewick and to all the great qLibc users.
    -
    76 *
    -
    77 * Additional features:
    -
    78 * - iterator.
    -
    79 * - find nearest key.
    -
    80 * - iteration from given key.
    -
    81 * - find min/max key.
    -
    82 *
    -
    83 * @code
    -
    84 * qtreetbl_t *tbl = qtreetbl(QTREETBL_THREADSAFE);
    -
    85 *
    -
    86 * tbl->put(tbl, "KEY", "DATA", 4); // use putobj() for binary keys.
    -
    87 * void *data = tbl->get(tbl, "KEY", false); // use getobj() for binary keys.
    -
    88 * tbl->remove(tbl, "KEY"); // use remove_by_key() for binary keys.
    -
    89 *
    -
    90 * // iteration example
    -
    91 * qtreetbl_obj_t obj;
    -
    92 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    93 * tbl->lock(tbl); // for thread safety
    -
    94 * while (tbl->getnext(tbl, &obj, false) == true) {
    -
    95 * ...
    -
    96 * }
    -
    97 * tbl->unlock(tbl);
    -
    98 *
    -
    99 * // find a nearest key then iterate from there
    -
    100 * tbl->lock(tbl);
    -
    101 * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "K", sizeof("K"), false);
    -
    102 * tbl->lock(tbl); // for thread safety
    -
    103 * while (tbl->getnext(tbl, &obj, false) == true) {
    -
    104 * ...
    -
    105 * }
    -
    106 * tbl->unlock(tbl);
    -
    107 *
    -
    108 * // find minimum value using custom comparator
    -
    109 * tbl->set_compare(tbl, my_compare_func); // default is byte comparator
    -
    110 * void *min = tbl->find_min(tbl, &keysize);
    -
    111 *
    -
    112 * // get total number of objects in the table
    -
    113 * size_t num = tbl->size(tbl);
    -
    114 *
    -
    115 * tbl->free(tbl);
    -
    116 * @endcode
    -
    117 */
    -
    118
    -
    119#include <stdio.h>
    -
    120#include <stdlib.h>
    -
    121#include <stdbool.h>
    -
    122#include <string.h>
    -
    123#include <stdarg.h>
    -
    124#include <errno.h>
    -
    125#include <assert.h>
    -
    126#include "qinternal.h"
    -
    127#include "utilities/qstring.h"
    -
    128#include "containers/qtreetbl.h"
    -
    129
    -
    130#ifndef _DOXYGEN_SKIP
    -
    131
    -
    132/* internal functions */
    -
    133#define LLRB234 // comment to build 2-3 variant
    -
    134static bool is_red(qtreetbl_obj_t *obj);
    -
    135static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj);
    -
    136static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj);
    -
    137static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj);
    -
    138static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj);
    -
    139static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj);
    -
    140static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj);
    -
    141static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj);
    -
    142static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj);
    -
    143static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj);
    -
    144static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
    -
    145 size_t namesize);
    -
    146static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
    -
    147 const void *data, size_t datasize);
    -
    148static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    -
    149 const void *name, size_t namesize,
    -
    150 const void *data, size_t datasize);
    -
    151static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    -
    152 const void *name, size_t namesize);
    -
    153static void free_objs(qtreetbl_obj_t *obj);
    -
    154static uint8_t reset_iterator(qtreetbl_t *tbl);
    -
    155
    -
    156struct branch_obj_s {
    -
    157 struct branch_obj_s *p;
    -
    158 char *s;
    -
    159};
    -
    160static void print_branch(struct branch_obj_s *branch, FILE *out);
    -
    161static void print_node(qtreetbl_obj_t *obj, FILE *out, struct branch_obj_s *prev,
    -
    162 bool right);
    -
    163#endif
    -
    164
    -
    165/**
    -
    166 * Initialize a tree table.
    -
    167 *
    -
    168 * @param options combination of initialization options.
    -
    169 *
    -
    170 * @return a pointer of malloced qtreetbl_t, otherwise returns NULL.
    -
    171 * @retval errno will be set in error condition.
    -
    172 * - ENOMEM : Memory allocation failure.
    -
    173 *
    -
    174 * @code
    -
    175 * qtreetbl_t *tbl = qtreetbl(0);
    -
    176 * @endcode
    -
    177 *
    -
    178 * @note
    -
    179 * Available options:
    -
    180 * - QTREETBL_THREADSAFE - make it thread-safe.
    -
    181 */
    -
    182qtreetbl_t *qtreetbl(int options) {
    -
    183 qtreetbl_t *tbl = (qtreetbl_t *) calloc(1, sizeof(qtreetbl_t));
    -
    184 if (tbl == NULL)
    -
    185 goto malloc_failure;
    -
    186
    -
    187 // handle options.
    -
    188 if (options & QTREETBL_THREADSAFE) {
    -
    189 Q_MUTEX_NEW(tbl->qmutex, true);
    -
    190 if (tbl->qmutex == NULL)
    -
    191 goto malloc_failure;
    -
    192 }
    -
    193
    -
    194 // assign methods
    -
    195 tbl->set_compare = qtreetbl_set_compare;
    -
    196
    -
    197 tbl->put = qtreetbl_put;
    -
    198 tbl->putstr = qtreetbl_putstr;
    -
    199 tbl->putstrf = qtreetbl_putstrf;
    -
    200 tbl->putobj = qtreetbl_putobj;
    -
    201
    -
    202 tbl->get = qtreetbl_get;
    -
    203 tbl->getstr = qtreetbl_getstr;
    -
    204 tbl->getobj = qtreetbl_getobj;
    -
    205
    -
    206 tbl->remove = qtreetbl_remove;
    -
    207 tbl->removeobj = qtreetbl_removeobj;
    -
    208
    -
    209 tbl->getnext = qtreetbl_getnext;
    -
    210
    -
    211 tbl->find_min = qtreetbl_find_min;
    -
    212 tbl->find_max = qtreetbl_find_max;
    -
    213 tbl->find_nearest = qtreetbl_find_nearest;
    -
    214
    -
    215 tbl->size = qtreetbl_size;
    -
    216 tbl->clear = qtreetbl_clear;
    -
    217
    -
    218 tbl->lock = qtreetbl_lock;
    -
    219 tbl->unlock = qtreetbl_unlock;
    -
    220
    -
    221 tbl->free = qtreetbl_free;
    -
    222 tbl->debug = qtreetbl_debug;
    -
    223
    -
    224 // Set default comparison function.
    -
    225 qtreetbl_set_compare(tbl, qtreetbl_byte_cmp);
    -
    226 reset_iterator(tbl);
    -
    227
    -
    228 return tbl;
    -
    229
    -
    230 malloc_failure:
    -
    231 errno = ENOMEM;
    -
    232 if (tbl != NULL) {
    -
    233 assert(tbl->qmutex == NULL);
    -
    234 qtreetbl_free(tbl);
    -
    235 }
    -
    236 return NULL;
    -
    237}
    -
    238
    -
    239/**
    -
    240 * qtreetbl->set_compare(): Set user comparator.
    -
    241 *
    -
    242 * @param tbl qtreetbl_t container pointer.
    -
    243 * @param cmp a pointer to the user comparator function.
    -
    244 *
    -
    245 * @note
    -
    246 * By default, qtreetbl uses byte comparator that works for
    -
    247 * both binary type key and string type key. Please refer
    -
    248 * qtreetbl_byte_cmp() for your idea to make your own comparator.
    -
    249 */
    -
    250void qtreetbl_set_compare(qtreetbl_t *tbl,
    -
    251 int (*cmp)(const void *name1, size_t namesize1,
    -
    252 const void *name2, size_t namesize2)) {
    -
    253 tbl->compare = cmp;
    -
    254}
    -
    255
    -
    256/**
    -
    257 * qtreetbl->put(): Put an object into this table with string type key.
    -
    258 *
    -
    259 * @param tbl qtreetbl_t container pointer.
    -
    260 * @param name key name.
    -
    261 * @param data data object.
    -
    262 * @param datasize size of data object.
    -
    263 *
    -
    264 * @return true if successful, otherwise returns false.
    -
    265 * @retval errno will be set in error condition.
    -
    266 * - EINVAL : Invalid argument.
    -
    267 * - ENOMEM : Memory allocation failure.
    -
    268 */
    -
    269bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data,
    -
    270 size_t datasize) {
    -
    271 return qtreetbl_putobj(tbl,
    -
    272 name, (name != NULL) ? (strlen(name) + 1) : 0, data, datasize);
    -
    273}
    -
    274
    -
    275/**
    -
    276 * qtreetbl->putstr(): Put a string into this table.
    -
    277 *
    -
    278 * @param tbl qtreetbl container pointer.
    -
    279 * @param name key name.
    -
    280 * @param str string data.
    -
    281 *
    -
    282 * @return true if successful, otherwise returns false.
    -
    283 * @retval errno will be set in error condition.
    -
    284 * - EINVAL : Invalid argument.
    -
    285 * - ENOMEM : Memory allocation failure.
    -
    286 */
    -
    287bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str) {
    -
    288 return qtreetbl_putobj(tbl,
    -
    289 name, (name != NULL) ? (strlen(name) + 1) : 0,
    -
    290 str, (str != NULL) ? (strlen(str) + 1) : 0);
    -
    291}
    -
    292
    -
    293/**
    -
    294 * qtreetbl->putstrf(): Put a formatted string into this table.
    -
    295 *
    -
    296 * @param tbl qtreetbl_t container pointer.
    -
    297 * @param name key name.
    -
    298 * @param format formatted string data.
    -
    299 *
    -
    300 * @return true if successful, otherwise returns false.
    -
    301 * @retval errno will be set in error condition.
    -
    302 * - EINVAL : Invalid argument.
    -
    303 * - ENOMEM : Memory allocation failure.
    -
    304 */
    -
    305bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,
    -
    306 ...) {
    -
    307 char *str;
    -
    308 DYNAMIC_VSPRINTF(str, format);
    -
    309 if (str == NULL) {
    -
    310 errno = ENOMEM;
    -
    311 return false;
    -
    312 }
    -
    313
    -
    314 bool ret = qtreetbl_putstr(tbl, name, str);
    -
    315 free(str);
    -
    316 return ret;
    -
    317}
    -
    318
    -
    319/**
    -
    320 * qtreetbl->putobj(): Put an object data into this table with an object key.
    -
    321 *
    -
    322 * @param tbl qtreetbl_t container pointer.
    -
    323 * @param name key name.
    -
    324 * @param namesize key size.
    -
    325 * @param data data object.
    -
    326 * @param datasize size of data object.
    -
    327 *
    -
    328 * @return true if successful, otherwise returns false.
    -
    329 * @retval errno will be set in error condition.
    -
    330 * - EINVAL : Invalid argument.
    -
    331 * - ENOMEM : Memory allocation failure.
    -
    332 *
    -
    333 * @note
    -
    334 * This is the underlying put function which all other put methods use.
    -
    335 */
    -
    336bool qtreetbl_putobj(qtreetbl_t *tbl, const void *name, size_t namesize,
    -
    337 const void *data, size_t datasize) {
    -
    338 if (name == NULL || namesize == 0) {
    -
    339 errno = EINVAL;
    -
    340 return false;
    -
    341 }
    -
    342
    -
    343 qtreetbl_lock(tbl);
    -
    344 errno = 0;
    -
    345 qtreetbl_obj_t *root = put_obj(tbl, tbl->root, name, namesize, data,
    -
    346 datasize);
    -
    347 if (root == NULL || errno == ENOMEM) {
    -
    348 qtreetbl_unlock(tbl);
    -
    349 return false;
    -
    350 }
    -
    351 root->red = false;
    -
    352 tbl->root = root;
    -
    353 qtreetbl_unlock(tbl);
    -
    354
    -
    355 return true;
    -
    356}
    -
    357
    -
    358/**
    -
    359 * qtreetbl->get(): Get an object from this table.
    -
    360 *
    -
    361 * @param tbl qtreetbl_t container pointer.
    -
    362 * @param name key name.
    -
    363 * @param datasize if not NULL, object size will be stored.
    -
    364 * @param newmem whether or not to allocate memory for the data.
    -
    365 *
    -
    366 * @return a pointer of data if the key is found, otherwise returns NULL.
    -
    367 * @retval errno will be set in error condition.
    -
    368 * - ENOENT : No such key found.
    -
    369 * - EINVAL : Invalid argument.
    -
    370 * - ENOMEM : Memory allocation failure.
    -
    371 *
    -
    372 * @code
    -
    373 * qtreetbl_t *tbl = qtreetbl(0);
    -
    374 * (...codes...)
    -
    375 *
    -
    376 * // with newmem flag unset
    -
    377 * size_t size;
    -
    378 * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false);
    -
    379 *
    -
    380 * // with newmem flag set
    -
    381 * size_t size;
    -
    382 * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true);
    -
    383 * free(data);
    -
    384 * @endcode
    -
    385 *
    -
    386 * @note
    -
    387 * If newmem flag is set, returned data will be malloced and should be
    -
    388 * deallocated by user. Otherwise returned pointer will point internal buffer
    -
    389 * directly and should not be de-allocated by user. In thread-safe mode,
    -
    390 * newmem flag must be set to true always.
    -
    391 */
    -
    392void *qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize,
    -
    393 bool newmem) {
    -
    394 return qtreetbl_getobj(tbl,
    -
    395 name, (name != NULL) ? (strlen(name) + 1) : 0, datasize, newmem);
    -
    396}
    -
    397
    -
    398/**
    -
    399 * qtreetbl->getstr(): Finds an object and returns it as string type.
    -
    400 *
    -
    401 * @param tbl qtreetbl_t container pointer.
    -
    402 * @param name key name.
    -
    403 * @param newmem whether or not to allocate memory for the data.
    -
    404 *
    -
    405 * @return a pointer to data if the key is found, otherwise returns NULL.
    -
    406 * @retval errno will be set in error condition.
    -
    407 * - ENOENT : No such key found.
    -
    408 * - EINVAL : Invalid argument.
    -
    409 * - ENOMEM : Memory allocation failure.
    -
    410 *
    -
    411 * @note
    -
    412 * If newmem flag is set, returned data will be malloced and should be
    -
    413 * deallocated by user. Otherwise returned pointer will point internal buffer
    -
    414 * directly and should not be de-allocated by user. In thread-safe mode,
    -
    415 * newmem flag must be set to true always.
    -
    416 */
    -
    417char *qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem) {
    -
    418 return qtreetbl_getobj(tbl,
    -
    419 name, (name != NULL) ? (strlen(name) + 1) : 0, NULL, newmem);
    -
    420}
    -
    421
    -
    422/**
    -
    423 * qtreetbl->getobj(): Get an object from this table with an object name.
    -
    424 *
    -
    425 * @param tbl qtreetbl_t container pointer.
    -
    426 * @param name key name.
    -
    427 * @param namesize key size.
    -
    428 * @param datasize if not NULL, oject size will be stored.
    -
    429 * @param newmem whether or not to allocate memory for the data.
    -
    430 *
    -
    431 * @return a pointer of data if the key is found, otherwise returns NULL.
    -
    432 * @retval errno will be set in error condition.
    -
    433 * - ENOENT : No such key found.
    -
    434 * - EINVAL : Invalid argument.
    -
    435 * - ENOMEM : Memory allocation failure.
    -
    436 *
    -
    437 * @note
    -
    438 * If newmem flag is set, returned data will be malloced and should be
    -
    439 * deallocated by user. Otherwise returned pointer will point internal buffer
    -
    440 * directly and should not be de-allocated by user. In thread-safe mode,
    -
    441 * newmem flag must be set to true always.
    -
    442 */
    -
    443void *qtreetbl_getobj(qtreetbl_t *tbl, const void *name, size_t namesize,
    -
    444 size_t *datasize, bool newmem) {
    -
    445 if (name == NULL || namesize == 0) {
    -
    446 errno = EINVAL;
    -
    447 return NULL;
    -
    448 }
    -
    449
    -
    450 qtreetbl_lock(tbl);
    -
    451 qtreetbl_obj_t *obj = find_obj(tbl, name, namesize);
    -
    452 void *data = NULL;
    -
    453 if (obj != NULL) {
    -
    454 data = (newmem) ? qmemdup(obj->data, obj->datasize) : obj->data;
    -
    455 if (datasize != NULL) {
    -
    456 *datasize = (data != NULL) ? obj->datasize : 0;
    -
    457 }
    -
    458 }
    -
    459 qtreetbl_unlock(tbl);
    -
    460 return data;
    -
    461}
    -
    462
    -
    463/**
    -
    464 * qtreetbl->remove(): Remove an object from this table.
    -
    465 *
    -
    466 * @param tbl qtreetbl_t container pointer.
    -
    467 * @param name key name.
    -
    468 *
    -
    469 * @return true if successful, otherwise(not found) returns false.
    -
    470 * @retval errno will be set in error condition.
    -
    471 * - ENOENT : No such key found.
    -
    472 * - EINVAL : Invalid argument.
    -
    473 */
    -
    474bool qtreetbl_remove(qtreetbl_t *tbl, const char *name) {
    -
    475 return qtreetbl_removeobj(tbl,
    -
    476 name, (name != NULL) ? strlen(name) + 1 : 0);
    -
    477}
    -
    478
    -
    479/**
    -
    480 * qtreetbl->remove(): Remove an object from this table with an object name.
    -
    481 *
    -
    482 * @param tbl qtreetbl_t container pointer.
    -
    483 * @param name key name.
    -
    484 * @param name key size.
    -
    485 *
    -
    486 * @return true if successful, otherwise(not found) returns false.
    -
    487 * @retval errno will be set in error condition.
    -
    488 * - ENOENT : No such key found.
    -
    489 * - EINVAL : Invalid argument.
    -
    490 */
    -
    491bool qtreetbl_removeobj(qtreetbl_t *tbl, const void *name, size_t namesize) {
    -
    492 if (name == NULL) {
    -
    493 errno = EINVAL;
    -
    494 return false;
    -
    495 }
    -
    496
    -
    497 qtreetbl_lock(tbl);
    -
    498 errno = 0;
    -
    499 tbl->root = remove_obj(tbl, tbl->root, name, namesize);
    -
    500 if (tbl->root != NULL) {
    -
    501 tbl->root->red = false;
    -
    502 }
    -
    503 bool removed = (errno != ENOENT) ? true : false;
    -
    504 qtreetbl_unlock(tbl);
    -
    505
    -
    506 return removed;
    -
    507}
    -
    508
    -
    509/**
    -
    510 * qtreetbl->getnext(): Get next element.
    -
    511 *
    -
    512 * @param tbl qtreetbl_t container pointer.
    -
    513 * @param obj found data will be stored in this object.
    -
    514 * @param newmem whether or not to allocate memory for the data.
    -
    515 *
    -
    516 * @return true if found otherwise returns false.
    -
    517 * @retval errno will be set in error condition.
    -
    518 * - ENOENT : No next element.
    -
    519 * - EINVAL : Invalid argument.
    -
    520 * - ENOMEM : Memory allocation failure.
    -
    521 *
    -
    522 * @code
    -
    523 * [Iteration example from the beginning]
    -
    524 *
    -
    525 * qtreetbl_obj_t obj;
    -
    526 * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    -
    527 * tbl->lock(tbl); // lock it when thread condition is expected
    -
    528 * while(tbl->getnext(tbl, &obj, false) == true) {
    -
    529 * //
    -
    530 * // obj.name : key data
    -
    531 * // obj.namesize : key size
    -
    532 * // obj.data : data
    -
    533 * // obj.datasize : data size
    -
    534 * //
    -
    535 * // Do free obj.name and obj.data if newmem is set to true;
    -
    536 * }
    -
    537 * tbl->unlock(tbl);
    -
    538 * @endcode
    -
    539 *
    -
    540 * @code
    -
    541 * [Iteration example from given point]
    -
    542 *
    -
    543 * tbl->lock(tbl); // to guarantee no table update during the run
    -
    544 * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "F", sizeof("F"), false);
    -
    545 * while (tbl->getnext(tbl, &obj, false) == true) { // newmem is false
    -
    546 * // If a tree has 5 objects, A, C, E, G and I.
    -
    547 * // The iteration sequence from nearest "F" will be: E->G->I->A->C
    -
    548 * }
    -
    549 * tbl->unlock(tbl);
    -
    550 *
    -
    551 * @endcode
    -
    552 *
    -
    553 * @code
    -
    554 * [Removal example in iteration loop]
    -
    555 *
    -
    556 * qtreetbl_obj_t obj;
    -
    557 * memset((void*)&obj, 0, sizeof(obj));
    -
    558 * tbl->lock(tbl);
    -
    559 * while (tbl->getnext(tbl, &obj, false) == true) {
    -
    560 * if (...condition...) {
    -
    561 * char *name = qmemdup(obj.name, obj.namesize); // keep the name
    -
    562 * size_t namesize = obj.namesize; // for removal argument
    -
    563 * tbl->removeobj(tbl, obj.name, obj.namesize); // remove
    -
    564 * obj = tbl->find_nearest(tbl, name, namesize, false); // rewind one step back
    -
    565 * free(name); // clean up
    -
    566 * }
    -
    567 * }
    -
    568 * tbl->unlock(tbl);
    -
    569 * @endcode
    -
    570 *
    -
    571 * @note
    -
    572 * - Data insertion or deletion can be made during the traversal, but in that
    -
    573 * case iterator doesn't guarantee full sweep and possibly skip some visits.
    -
    574 * When deletion happens in getnext() loop, use find_nearest() to rewind the
    -
    575 * iterator one step back.
    -
    576 * - Object obj should be initialized with 0 by using memset() before first call.
    -
    577 * - If newmem flag is true, user should de-allocate obj.name and obj.data
    -
    578 * resources.
    -
    579 */
    -
    580bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem) {
    -
    581 if (obj == NULL) {
    -
    582 errno = EINVAL;
    -
    583 return NULL;
    -
    584 }
    -
    585
    -
    586 uint8_t tid = obj->tid;
    -
    587 if (obj->next == NULL) { // first time call
    -
    588 if (tbl->root == NULL) {
    -
    589 return false;
    -
    590 }
    -
    591 // get a new iterator id
    -
    592 tid = reset_iterator(tbl);;
    -
    593 }
    -
    594
    -
    595 qtreetbl_obj_t *cursor = ((obj->next != NULL) ? obj->next : tbl->root);
    -
    596 while (cursor != NULL) {
    -
    597 if (cursor->left != NULL && cursor->left->tid != tid) {
    -
    598 cursor->left->next = cursor;
    -
    599 cursor = cursor->left;
    -
    600 continue;
    -
    601 } else if (cursor->tid != tid) {
    -
    602 cursor->tid = tid;
    -
    603 *obj = *cursor;
    -
    604 if (newmem) {
    -
    605 obj->name = qmemdup(cursor->name, cursor->namesize);
    -
    606 obj->data = qmemdup(cursor->data, cursor->datasize);
    -
    607 }
    -
    608 obj->next = cursor; // store original address in tree for next iteration
    -
    609 return true;
    -
    610 } else if (cursor->right != NULL && cursor->right->tid != tid) {
    -
    611 cursor->right->next = cursor;
    -
    612 cursor = cursor->right;
    -
    613 continue;
    -
    614 }
    -
    615 cursor = cursor->next;
    -
    616 }
    -
    617
    -
    618 // end of travel, reset iterator to allow iteration start over in next call
    -
    619 reset_iterator(tbl);
    -
    620 return false;
    -
    621}
    -
    622
    -
    623/**
    -
    624 * qtreetbl->find_min(): Find the name of the leftmost object.
    -
    625 *
    -
    626 * @param tbl qtreetbl_t container pointer.
    -
    627 * @param namesize if not NULL, the size of key name will be stored.
    -
    628 *
    -
    629 * @return malloced memory copying the key name.
    -
    630 *
    -
    631 * @note
    -
    632 * It's user's responsibility to free the return.
    -
    633 */
    -
    634void *qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize) {
    -
    635 qtreetbl_lock(tbl);
    -
    636 qtreetbl_obj_t *obj = find_min(tbl->root);
    -
    637 if (obj == NULL) {
    -
    638 errno = ENOENT;
    -
    639 qtreetbl_unlock(tbl);
    -
    640 return NULL;
    -
    641 }
    -
    642
    -
    643 if (namesize != NULL) {
    -
    644 *namesize = obj->namesize;
    -
    645 }
    -
    646 void *name = qmemdup(obj->name, obj->namesize);
    -
    647 qtreetbl_unlock(tbl);
    -
    648 return name;
    -
    649}
    -
    650
    -
    651/**
    -
    652 * qtreetbl->find_max(): Find the name of the rightmost object.
    -
    653 *
    -
    654 * @param tbl qtreetbl_t container pointer.
    -
    655 * @param namesize if not NULL, the size of key name will be stored.
    -
    656 *
    -
    657 * @return malloced memory copying the key name.
    -
    658 *
    -
    659 * @note
    -
    660 * It's user's responsibility to free the return.
    -
    661 */
    -
    662void *qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize) {
    -
    663 qtreetbl_lock(tbl);
    -
    664 qtreetbl_obj_t *obj = find_max(tbl->root);
    -
    665 if (obj == NULL) {
    -
    666 errno = ENOENT;
    -
    667 qtreetbl_unlock(tbl);
    -
    668 return NULL;
    -
    669 }
    -
    670
    -
    671 if (namesize != NULL) {
    -
    672 *namesize = obj->namesize;
    -
    673 }
    -
    674 void *name = qmemdup(obj->name, obj->namesize);
    -
    675 qtreetbl_unlock(tbl);
    -
    676 return name;
    -
    677}
    -
    678
    -
    679/**
    -
    680 * qtreetbl->find_nearest(): Find an object with matching key or nearest.
    -
    681 *
    -
    682 * find_nearest() returns matching key or nearest key object. If there's
    -
    683 * no keys in the table. It'll return empty qtreetbl_obj_t object
    -
    684 * with errno ENOENT.
    -
    685 *
    -
    686 * @param tbl qtreetbl_t container pointer.
    -
    687 * @param name key name.
    -
    688 * @param namesize key size.
    -
    689 * @param newmem whether or not to allocate memory for the data.
    -
    690 *
    -
    691 * @return qtreetbl_obj_t object.
    -
    692 *
    -
    693 * @retval errno will be set in error condition.
    -
    694 * - ENOENT : No next element.
    -
    695 * - EINVAL : Invalid argument.
    -
    696 * - ENOMEM : Memory allocation failure.
    -
    697 *
    -
    698 * @code
    -
    699 * Data Set : A B C D E I N R S X
    -
    700 * find_nearest("0") => "A" // no smaller key available, so "A"
    -
    701 * find_nearest("C") => "C" // matching key found
    -
    702 * find_nearest("F") => "E" // "E" is nearest smaller key from "F"
    -
    703 * @endcode
    -
    704 *
    -
    705 * @note
    -
    706 * When there's no matching key it look for closest smaller key
    -
    707 * in the neighbors. The only exception when it returns bigger key
    -
    708 * than given search key is that when there's no smaller keys available
    -
    709 * in the table. In such case, it'll return the nearest bigger key.
    -
    710 */
    -
    711qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name,
    -
    712 size_t namesize, bool newmem) {
    -
    713 qtreetbl_obj_t retobj;
    -
    714 memset((void*) &retobj, 0, sizeof(retobj));
    -
    715
    -
    716 if (name == NULL || namesize == 0) {
    -
    717 errno = EINVAL;
    -
    718 return retobj;
    -
    719 }
    -
    720
    -
    721 qtreetbl_lock(tbl);
    -
    722 qtreetbl_obj_t *obj, *lastobj;
    -
    723 for (obj = lastobj = tbl->root; obj != NULL;) {
    -
    724 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    725 if (cmp == 0) {
    -
    726 break;
    -
    727 }
    -
    728 lastobj = obj;
    -
    729 if (cmp < 0) {
    -
    730 if (obj->left != NULL) {
    -
    731 obj->left->next = obj;
    -
    732 }
    -
    733 obj = obj->left;
    -
    734 } else {
    -
    735 if (obj->right != NULL) {
    -
    736 obj->right->next = obj;
    -
    737 }
    -
    738 obj = obj->right;
    -
    739 }
    -
    740 }
    -
    741
    -
    742 if (obj == NULL) {
    -
    743 for (obj = lastobj;
    -
    744 obj != NULL && (tbl->compare(name, namesize, obj->name, obj->namesize) < 0);
    -
    745 obj = obj->next);
    -
    746 if (obj == NULL) {
    -
    747 obj = lastobj;
    -
    748 }
    -
    749 }
    -
    750
    -
    751 if (obj != NULL) {
    -
    752 retobj = *obj;
    -
    753 if (newmem) {
    -
    754 retobj.name = qmemdup(obj->name, obj->namesize);
    -
    755 retobj.data = qmemdup(obj->data, obj->datasize);
    -
    756 }
    -
    757 // set travel info to be used for iteration in getnext()
    -
    758 retobj.tid = tbl->tid;
    -
    759 retobj.next = obj;
    -
    760 } else {
    -
    761 errno = ENOENT;
    -
    762 }
    -
    763
    -
    764 qtreetbl_unlock(tbl);
    -
    765 return retobj;
    -
    766}
    -
    767
    -
    768/**
    -
    769 * qtreetbl->size(): Returns the number of keys in the table.
    -
    770 *
    -
    771 * @param tbl qtreetbl_t container pointer.
    -
    772 *
    -
    773 * @return number of elements stored.
    -
    774 */
    -
    775size_t qtreetbl_size(qtreetbl_t *tbl) {
    -
    776 return tbl->num;
    -
    777}
    -
    778
    -
    779/**
    -
    780 * qtreetbl->clear(): Clears the table so that it contains no keys.
    -
    781 *
    -
    782 * @param tbl qtreetbl_t container pointer.
    -
    783 */
    -
    784void qtreetbl_clear(qtreetbl_t *tbl) {
    -
    785 qtreetbl_lock(tbl);
    -
    786 free_objs(tbl->root);
    -
    787 tbl->root = NULL;
    -
    788 tbl->num = 0;
    -
    789 qtreetbl_unlock(tbl);
    -
    790}
    -
    791
    -
    792/**
    -
    793 * qtreetbl->lock(): Enter critical section.
    -
    794 *
    -
    795 * @param tbl qtreetbl_t container pointer.
    -
    796 *
    -
    797 * @note
    -
    798 * From user side, normally locking operation is only needed when traverse
    -
    799 * all elements using qtreetbl->getnext().
    -
    800 *
    -
    801 * @note
    -
    802 * This operation will do nothing if QTREETBL_THREADSAFE option was not
    -
    803 * given at the initialization time.
    -
    804 */
    -
    805void qtreetbl_lock(qtreetbl_t *tbl) {
    -
    806 Q_MUTEX_ENTER(tbl->qmutex);
    -
    807}
    -
    808
    -
    809/**
    -
    810 * qtreetbl->unlock(): Leave critical section.
    -
    811 *
    -
    812 * @param tbl qtreetbl_t container pointer.
    -
    813 *
    -
    814 * @note
    -
    815 * This operation will do nothing if QTREETBL_THREADSAFE option was not
    -
    816 * given at the initialization time.
    -
    817 */
    -
    818void qtreetbl_unlock(qtreetbl_t *tbl) {
    -
    819 Q_MUTEX_LEAVE(tbl->qmutex);
    -
    820}
    -
    821
    -
    822/**
    -
    823 * qtreetbl->free(): De-allocate the table.
    -
    824 *
    -
    825 * @param tbl qtreetbl_t container pointer.
    -
    826 */
    -
    827void qtreetbl_free(qtreetbl_t *tbl) {
    -
    828 qtreetbl_clear(tbl);
    -
    829 Q_MUTEX_DESTROY(tbl->qmutex);
    -
    830 free(tbl);
    -
    831}
    -
    832
    -
    833int qtreetbl_byte_cmp(const void *name1, size_t namesize1, const void *name2,
    -
    834 size_t namesize2) {
    -
    835 size_t minsize = (namesize1 < namesize2) ? namesize1 : namesize2;
    -
    836 int cmp = memcmp(name1, name2, minsize);
    -
    837 if (cmp != 0 || namesize1 == namesize2) {
    -
    838 return cmp;
    -
    839 }
    -
    840 return (namesize1 < namesize2) ? -1 : +1;
    -
    841}
    -
    842
    -
    843/**
    -
    844 * qtreetbl->debug(): Print the internal tree structure in text.
    -
    845 *
    -
    846 * @param tbl qtreetbl_t container pointer.
    -
    847 * @param out output stream.
    -
    848 *
    -
    849 * @return true if successful, otherwise returns false.
    -
    850 * @retval errno will be set in error condition.
    -
    851 * - EIO : Invalid output stream.
    -
    852 *
    -
    853 * @code
    -
    854 * Example output:
    -
    855 *
    -
    856 * ┌───9
    -
    857 * │ └──[8]
    -
    858 * ┌───7
    -
    859 * │ │ ┌───6
    -
    860 * │ └──[5]
    -
    861 * │ └───4
    -
    862 * 3
    -
    863 * │ ┌───2
    -
    864 * └───1
    -
    865 * └───0
    -
    866 * @endcode
    -
    867 * @note
    -
    868 * Red nodes are wrapped in `[]`.
    -
    869 * In this example, 5 and 8 are Red nodes.
    -
    870 */
    -
    871bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out) {
    -
    872 if (out == NULL) {
    -
    873 errno = EIO;
    -
    874 return false;
    -
    875 }
    -
    876
    -
    877 qtreetbl_lock(tbl);
    -
    878 print_node(tbl->root, out, NULL, false);
    -
    879 qtreetbl_unlock(tbl);
    -
    880 return true;
    -
    881}
    -
    882
    -
    883/**
    -
    884 * Verifies that root property of the red-black tree is satisfied.
    -
    885 *
    -
    886 * Root property: The root is black.
    -
    887 *
    -
    888 * @param tbl qtreetbl_t container pointer.
    -
    889 */
    -
    890int node_check_root(qtreetbl_t *tbl) {
    -
    891 if (tbl == NULL) {
    -
    892 return 1;
    -
    893 }
    -
    894
    -
    895 if (is_red(tbl->root)) {
    -
    896 return 1;
    -
    897 }
    -
    898 return 0;
    -
    899}
    -
    900
    -
    901/**
    -
    902 * Verifies that red property of the red-black tree is satisfied.
    -
    903 *
    -
    904 * Red property: If a node is red, then both its children are black.
    -
    905 *
    -
    906 * @param tbl qtreetbl_t container pointer.
    -
    907 * @param obj qtreetbl_obj_t object pointer.
    -
    908 */
    -
    909int node_check_red(qtreetbl_t *tbl, qtreetbl_obj_t *obj) {
    -
    910 if (obj == NULL) {
    -
    911 return 0;
    -
    912 }
    -
    913
    -
    914 if (is_red(obj)) {
    -
    915 if (is_red(obj->right) || is_red(obj->left)) {
    -
    916 return 1;
    -
    917 }
    -
    918 }
    -
    919
    -
    920 if (node_check_red(tbl, obj->right)) {
    -
    921 return 1;
    -
    922 }
    -
    923 if (node_check_red(tbl, obj->left)) {
    -
    924 return 1;
    -
    925 }
    -
    926
    -
    927 return 0;
    -
    928}
    -
    929
    -
    930/**
    -
    931 * Verifies that black property of the red-black tree is satisfied.
    -
    932 *
    -
    933 * Black property: For each node, all simple paths from the node to
    -
    934 * descendant leaves contain the same number of black nodes.
    -
    935 *
    -
    936 * @param tbl qtreetbl_t container pointer.
    -
    937 * @param obj qtreetbl_obj_t object pointer.
    -
    938 * @param path_len black path length.
    -
    939 */
    -
    940int node_check_black(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len) {
    -
    941 if (obj == NULL) {
    -
    942 *path_len = 1;
    -
    943 return 0;
    -
    944 }
    -
    945
    -
    946 int right_path_len;
    -
    947 if (node_check_black(tbl, obj->right, &right_path_len)) {
    -
    948 return 1;
    -
    949 }
    -
    950 int left_path_len;
    -
    951 if (node_check_black(tbl, obj->left, &left_path_len)) {
    -
    952 return 1;
    -
    953 }
    -
    954
    -
    955 if (right_path_len != left_path_len) {
    -
    956 return 1;
    -
    957 }
    -
    958 *path_len = (!is_red(obj)) ? (right_path_len + 1) : right_path_len;
    -
    959
    -
    960 return 0;
    -
    961}
    -
    962
    -
    963/**
    -
    964 * Verifies that LLRB property of the left-leaning red-black tree is satisfied.
    -
    965 *
    -
    966 * LLRB property: 3-nodes always lean to the left and 4-nodes are balanced.
    -
    967 *
    -
    968 * @param tbl qtreetbl_t container pointer.
    -
    969 * @param obj qtreetbl_obj_t object pointer.
    -
    970 */
    -
    971int node_check_llrb(qtreetbl_t *tbl, qtreetbl_obj_t *obj) {
    -
    972 if (obj == NULL) {
    -
    973 return 0;
    -
    974 }
    -
    975
    -
    976 if (is_red(obj->right) && !is_red(obj->left)) {
    -
    977 return 1;
    -
    978 }
    -
    979
    -
    980 if (node_check_llrb(tbl, obj->right)) {
    -
    981 return 1;
    -
    982 }
    -
    983 if (node_check_llrb(tbl, obj->left)) {
    -
    984 return 1;
    -
    985 }
    -
    986
    -
    987 return 0;
    -
    988}
    -
    989
    -
    990/**
    -
    991 * Verifies that the invariants of the red-black tree are satisfied.
    -
    992 *
    -
    993 * Root property: The root is black.
    -
    994 * Red property: If a node is red, then both its children are black.
    -
    995 * Black property: For each node, all simple paths from the node to
    -
    996 * descendant leaves contain the same number of black nodes.
    -
    997 * LLRB property: 3-nodes always lean to the left and 4-nodes are balanced.
    -
    998 *
    -
    999 * @param tbl qtreetbl_t container pointer.
    -
    1000 */
    -
    1001int qtreetbl_check(qtreetbl_t *tbl) {
    -
    1002 if (tbl == NULL) {
    -
    1003 return 0;
    -
    1004 }
    -
    1005
    -
    1006 if (node_check_root(tbl)) {
    -
    1007 return 1;
    -
    1008 }
    -
    1009 if (node_check_red(tbl, tbl->root)) {
    -
    1010 return 2;
    -
    1011 }
    -
    1012 int path_len = 0;
    -
    1013 if (node_check_black(tbl, tbl->root, &path_len)) {
    -
    1014 return 3;
    -
    1015 }
    -
    1016 if (node_check_llrb(tbl, tbl->root)) {
    -
    1017 return 4;
    -
    1018 }
    -
    1019
    -
    1020 return 0;
    -
    1021}
    -
    1022
    -
    1023#ifndef _DOXYGEN_SKIP
    -
    1024
    -
    1025static bool is_red(qtreetbl_obj_t *obj) {
    -
    1026 return (obj != NULL) ? obj->red : false;
    -
    1027}
    -
    1028
    -
    1029uint32_t _q_treetbl_flip_color_cnt = 0;
    -
    1030static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj) {
    -
    1031 obj->red = !(obj->red);
    -
    1032 obj->left->red = !(obj->left->red);
    -
    1033 obj->right->red = !(obj->right->red);
    -
    1034 _q_treetbl_flip_color_cnt++;
    -
    1035 return obj;
    -
    1036}
    -
    1037
    -
    1038uint32_t _q_treetbl_rotate_left_cnt = 0;
    -
    1039static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj) {
    -
    1040 qtreetbl_obj_t *x = obj->right;
    -
    1041 obj->right = x->left;
    -
    1042 x->left = obj;
    -
    1043 x->red = x->left->red;
    -
    1044 x->left->red = true;
    -
    1045 _q_treetbl_rotate_left_cnt++;
    -
    1046 return x;
    -
    1047}
    -
    1048
    -
    1049uint32_t _q_treetbl_rotate_right_cnt = 0;
    -
    1050static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj) {
    -
    1051 qtreetbl_obj_t *x = obj->left;
    -
    1052 obj->left = x->right;
    -
    1053 x->right = obj;
    -
    1054 x->red = x->right->red;
    -
    1055 x->right->red = true;
    -
    1056 _q_treetbl_rotate_right_cnt++;
    -
    1057 return x;
    -
    1058}
    -
    1059
    -
    1060static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj) {
    -
    1061 flip_color(obj);
    -
    1062 if (is_red(obj->right->left)) {
    -
    1063 obj->right = rotate_right(obj->right);
    -
    1064 obj = rotate_left(obj);
    -
    1065 flip_color(obj);
    -
    1066#ifdef LLRB234
    -
    1067 // 2-3-4 exclusive
    -
    1068 if (is_red(obj->right->right)) {
    -
    1069 obj->right = rotate_left(obj->right);
    -
    1070 }
    -
    1071#endif
    -
    1072 }
    -
    1073 return obj;
    -
    1074}
    -
    1075
    -
    1076static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj) {
    -
    1077 flip_color(obj);
    -
    1078 if (is_red(obj->left->left)) {
    -
    1079 obj = rotate_right(obj);
    -
    1080 flip_color(obj);
    -
    1081 }
    -
    1082 return obj;
    -
    1083}
    -
    1084
    -
    1085static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj) {
    -
    1086 if (obj == NULL) {
    -
    1087 errno = ENOENT;
    -
    1088 return NULL;
    -
    1089 }
    -
    1090
    -
    1091 for (; obj->left != NULL; obj = obj->left);
    -
    1092 return obj;
    -
    1093}
    -
    1094
    -
    1095static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj) {
    -
    1096 if (obj == NULL) {
    -
    1097 errno = ENOENT;
    -
    1098 return NULL;
    -
    1099 }
    -
    1100
    -
    1101 for (; obj->right != NULL; obj = obj->right);
    -
    1102 return obj;
    -
    1103}
    -
    1104
    -
    1105static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj) {
    -
    1106 if (obj->left == NULL) {
    -
    1107 // 3-nodes are left-leaning, so this is a leaf.
    -
    1108 free(obj->name);
    -
    1109 free(obj->data);
    -
    1110 return NULL;
    -
    1111 }
    -
    1112 if (!is_red(obj->left) && !is_red(obj->left->left)) {
    -
    1113 obj = move_red_left(obj);
    -
    1114 }
    -
    1115 obj->left = remove_min(obj->left);
    -
    1116 return fix(obj);
    -
    1117}
    -
    1118
    -
    1119static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj) {
    -
    1120 // rotate right red to left
    -
    1121 if (is_red(obj->right)) {
    -
    1122#ifdef LLRB234
    -
    1123 // 2-3-4 exclusive
    -
    1124 if (is_red(obj->right->left)) {
    -
    1125 obj->right = rotate_right(obj->right);
    -
    1126 }
    -
    1127#endif
    -
    1128 obj = rotate_left(obj);
    -
    1129 }
    -
    1130 // rotate left red-red to right
    -
    1131 if (is_red(obj->left) && is_red(obj->left->left)) {
    -
    1132 obj = rotate_right(obj);
    -
    1133 }
    -
    1134#ifndef LLRB234
    -
    1135 // split 4-nodes (2-3 exclusive)
    -
    1136 if (is_red(obj->left) && is_red(obj->right)) {
    -
    1137 flip_color(obj);
    -
    1138 }
    -
    1139#endif
    -
    1140 return obj;
    -
    1141}
    -
    1142
    -
    1143static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
    -
    1144 size_t namesize) {
    -
    1145 if (name == NULL || namesize == 0) {
    -
    1146 errno = EINVAL;
    -
    1147 return NULL;
    -
    1148 }
    -
    1149
    -
    1150 qtreetbl_obj_t *obj;
    -
    1151 for (obj = tbl->root; obj != NULL;) {
    -
    1152 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    1153 if (cmp == 0) {
    -
    1154 return obj;
    -
    1155 }
    -
    1156 obj = (cmp < 0) ? obj->left : obj->right;
    -
    1157 }
    -
    1158
    -
    1159 errno = ENOENT;
    -
    1160 return NULL;
    -
    1161}
    -
    1162
    -
    1163static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
    -
    1164 const void *data, size_t datasize) {
    -
    1165 qtreetbl_obj_t *obj = (qtreetbl_obj_t *) calloc(1, sizeof(qtreetbl_obj_t));
    -
    1166 void *copyname = qmemdup(name, namesize);
    -
    1167 void *copydata = qmemdup(data, datasize);
    -
    1168
    -
    1169 if (obj == NULL || copyname == NULL) {
    -
    1170 errno = ENOMEM;
    -
    1171 free(obj);
    -
    1172 free(copyname);
    -
    1173 free(copydata);
    -
    1174 return NULL;
    -
    1175 }
    -
    1176
    -
    1177 obj->red = red;
    -
    1178 obj->name = copyname;
    -
    1179 obj->namesize = namesize;
    -
    1180 obj->data = copydata;
    -
    1181 obj->datasize = datasize;
    -
    1182
    -
    1183 return obj;
    -
    1184}
    -
    1185
    -
    1186static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    -
    1187 const void *name, size_t namesize,
    -
    1188 const void *data, size_t datasize) {
    -
    1189 if (obj == NULL) {
    -
    1190 tbl->num++;
    -
    1191 return new_obj(true, name, namesize, data, datasize);
    -
    1192 }
    -
    1193
    -
    1194#ifdef LLRB234
    -
    1195 // split 4-nodes on the way down (2-3-4 exclusive)
    -
    1196 if (is_red(obj->left) && is_red(obj->right)) {
    -
    1197 flip_color(obj);
    -
    1198 }
    -
    1199#endif
    -
    1200
    -
    1201 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    1202 if (cmp == 0) { // existing key found
    -
    1203 void *copydata = qmemdup(data, datasize);
    -
    1204 if (copydata != NULL) {
    -
    1205 free(obj->data);
    -
    1206 obj->data = copydata;
    -
    1207 obj->datasize = datasize;
    -
    1208 }
    -
    1209 } else if (cmp < 0) {
    -
    1210 obj->left = put_obj(tbl, obj->left, name, namesize, data, datasize);
    -
    1211 } else {
    -
    1212 obj->right = put_obj(tbl, obj->right, name, namesize, data, datasize);
    -
    1213 }
    -
    1214
    -
    1215 // fix right-leaning reds on the way up
    -
    1216 if (is_red(obj->right) && !is_red(obj->left)) {
    -
    1217 obj = rotate_left(obj);
    -
    1218 }
    -
    1219
    -
    1220 // fix two reds in a row on the way up
    -
    1221 if (is_red(obj->left) && is_red(obj->left->left)) {
    -
    1222 obj = rotate_right(obj);
    -
    1223 }
    -
    1224
    -
    1225#ifndef LLRB234
    -
    1226 // split 4-nodes on the way up (2-3 exclusive)
    -
    1227 if (is_red(obj->left) && is_red(obj->right)) {
    -
    1228 flip_color(obj);
    -
    1229 }
    -
    1230#endif
    -
    1231
    -
    1232 // return new root
    -
    1233 return obj;
    -
    1234}
    -
    1235
    -
    1236static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    -
    1237 const void *name, size_t namesize) {
    -
    1238 if (obj == NULL) {
    -
    1239 errno = ENOENT;
    -
    1240 return NULL;
    -
    1241 }
    -
    1242
    -
    1243 int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    1244 if (cmp < 0) {
    -
    1245 // move red left
    -
    1246 if (obj->left != NULL
    -
    1247 && (!is_red(obj->left) && !is_red(obj->left->left))) {
    -
    1248 obj = move_red_left(obj);
    -
    1249 }
    -
    1250 // keep going down to the left
    -
    1251 obj->left = remove_obj(tbl, obj->left, name, namesize);
    -
    1252 } else { // right or equal
    -
    1253 bool recmp = false; // optimization to reduce duplicated comparisions
    -
    1254 if (is_red(obj->left)) {
    -
    1255 obj = rotate_right(obj);
    -
    1256 recmp = true;
    -
    1257 }
    -
    1258 // remove if equal at the bottom
    -
    1259 if (obj->right == NULL) {
    -
    1260 if (recmp) {
    -
    1261 cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    1262 recmp = false;
    -
    1263 }
    -
    1264 if (cmp == 0) {
    -
    1265 free(obj->name);
    -
    1266 free(obj->data);
    -
    1267 free(obj);
    -
    1268 tbl->num--;
    -
    1269 return NULL;
    -
    1270 }
    -
    1271 }
    -
    1272 // move red right
    -
    1273 if (obj->right != NULL
    -
    1274 && (!is_red(obj->right) && !is_red(obj->right->left))) {
    -
    1275 obj = move_red_right(obj);
    -
    1276 recmp = true;
    -
    1277 }
    -
    1278 // found in the middle
    -
    1279 if (recmp) {
    -
    1280 cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    -
    1281 }
    -
    1282 if (cmp == 0) {
    -
    1283 // copy min to this then remove min
    -
    1284 qtreetbl_obj_t *minobj = find_min(obj->right);
    -
    1285 assert(minobj != NULL);
    -
    1286 free(obj->name);
    -
    1287 free(obj->data);
    -
    1288 obj->name = qmemdup(minobj->name, minobj->namesize);
    -
    1289 obj->namesize = minobj->namesize;
    -
    1290 obj->data = qmemdup(minobj->data, minobj->datasize);
    -
    1291 obj->datasize = minobj->datasize;
    -
    1292 obj->right = remove_min(obj->right);
    -
    1293 tbl->num--;
    -
    1294 } else {
    -
    1295 // keep going down to the right
    -
    1296 obj->right = remove_obj(tbl, obj->right, name, namesize);
    -
    1297 }
    -
    1298 }
    -
    1299 // fix right-leaning red nodes on the way up
    -
    1300 return fix(obj);
    -
    1301}
    -
    1302
    -
    1303static void free_objs(qtreetbl_obj_t *obj) {
    -
    1304 if (obj == NULL) {
    -
    1305 return;
    -
    1306 }
    -
    1307
    -
    1308 free_objs(obj->left);
    -
    1309 free_objs(obj->right);
    -
    1310
    -
    1311 free(obj->name);
    -
    1312 free(obj->data);
    -
    1313 free(obj);
    -
    1314}
    -
    1315
    -
    1316static uint8_t reset_iterator(qtreetbl_t *tbl) {
    -
    1317 if (tbl->root != NULL) {
    -
    1318 tbl->root->next = NULL;
    -
    1319 }
    -
    1320 return (++tbl->tid);
    -
    1321}
    -
    1322
    -
    1323static void print_branch(struct branch_obj_s *branch, FILE *out) {
    -
    1324 if (branch == NULL) {
    -
    1325 return;
    -
    1326 }
    -
    1327 print_branch(branch->p, out);
    -
    1328 fprintf(out, "%s", branch->s);
    -
    1329}
    -
    1330
    -
    1331static void print_node(qtreetbl_obj_t *obj, FILE *out, struct branch_obj_s *prev,
    -
    1332 bool right) {
    -
    1333 if (obj == NULL) {
    -
    1334 return;
    -
    1335 }
    -
    1336
    -
    1337 struct branch_obj_s branch;
    -
    1338 branch.p = prev;
    -
    1339
    -
    1340 branch.s = (prev != NULL) ? (right) ? " " : "│ " : "";
    -
    1341 print_node(obj->right, out, &branch, true);
    -
    1342
    -
    1343 print_branch(prev, out);
    -
    1344 if (prev != NULL) {
    -
    1345 fprintf(out, "%s%s", (right) ? "┌──" : "└──", (obj->red) ? "[" : "─");
    -
    1346 }
    -
    1347 if (obj->data == NULL && obj->namesize == sizeof(uint32_t)) {
    -
    1348 fprintf(out, "%u", *(uint32_t *)obj->name);
    -
    1349 } else {
    -
    1350 _q_textout(out, obj->name, obj->namesize, 15);
    -
    1351 }
    -
    1352 fprintf(out, "%s", (obj->red) ? "]\n" : "\n");
    -
    1353
    -
    1354 branch.s = (prev != NULL) ? (right) ? "│ " : " " : "";
    -
    1355 print_node(obj->left, out, &branch, false);
    -
    1356}
    -
    1357
    -
    1358#endif
    -
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition qstring.c:413
    -
    qtreetbl_t * qtreetbl(int options)
    Initialize a tree table.
    Definition qtreetbl.c:182
    -
    void * qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
    qtreetbl->get(): Get an object from this table.
    Definition qtreetbl.c:392
    -
    int node_check_root(qtreetbl_t *tbl)
    Verifies that root property of the red-black tree is satisfied.
    Definition qtreetbl.c:890
    -
    void qtreetbl_free(qtreetbl_t *tbl)
    qtreetbl->free(): De-allocate the table.
    Definition qtreetbl.c:827
    -
    bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str)
    qtreetbl->putstr(): Put a string into this table.
    Definition qtreetbl.c:287
    -
    bool qtreetbl_remove(qtreetbl_t *tbl, const char *name)
    qtreetbl->remove(): Remove an object from this table.
    Definition qtreetbl.c:474
    -
    size_t qtreetbl_size(qtreetbl_t *tbl)
    qtreetbl->size(): Returns the number of keys in the table.
    Definition qtreetbl.c:775
    -
    bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data, size_t datasize)
    qtreetbl->put(): Put an object into this table with string type key.
    Definition qtreetbl.c:269
    -
    void * qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize)
    qtreetbl->find_max(): Find the name of the rightmost object.
    Definition qtreetbl.c:662
    -
    void qtreetbl_lock(qtreetbl_t *tbl)
    qtreetbl->lock(): Enter critical section.
    Definition qtreetbl.c:805
    -
    bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out)
    qtreetbl->debug(): Print the internal tree structure in text.
    Definition qtreetbl.c:871
    -
    bool qtreetbl_putobj(qtreetbl_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
    qtreetbl->putobj(): Put an object data into this table with an object key.
    Definition qtreetbl.c:336
    -
    void * qtreetbl_getobj(qtreetbl_t *tbl, const void *name, size_t namesize, size_t *datasize, bool newmem)
    qtreetbl->getobj(): Get an object from this table with an object name.
    Definition qtreetbl.c:443
    -
    void qtreetbl_clear(qtreetbl_t *tbl)
    qtreetbl->clear(): Clears the table so that it contains no keys.
    Definition qtreetbl.c:784
    -
    char * qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem)
    qtreetbl->getstr(): Finds an object and returns it as string type.
    Definition qtreetbl.c:417
    -
    void qtreetbl_unlock(qtreetbl_t *tbl)
    qtreetbl->unlock(): Leave critical section.
    Definition qtreetbl.c:818
    -
    int node_check_llrb(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
    Verifies that LLRB property of the left-leaning red-black tree is satisfied.
    Definition qtreetbl.c:971
    -
    qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name, size_t namesize, bool newmem)
    qtreetbl->find_nearest(): Find an object with matching key or nearest.
    Definition qtreetbl.c:711
    -
    int node_check_black(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len)
    Verifies that black property of the red-black tree is satisfied.
    Definition qtreetbl.c:940
    -
    int qtreetbl_check(qtreetbl_t *tbl)
    Verifies that the invariants of the red-black tree are satisfied.
    Definition qtreetbl.c:1001
    -
    void * qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize)
    qtreetbl->find_min(): Find the name of the leftmost object.
    Definition qtreetbl.c:634
    -
    bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,...)
    qtreetbl->putstrf(): Put a formatted string into this table.
    Definition qtreetbl.c:305
    -
    int node_check_red(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
    Verifies that red property of the red-black tree is satisfied.
    Definition qtreetbl.c:909
    -
    bool qtreetbl_removeobj(qtreetbl_t *tbl, const void *name, size_t namesize)
    qtreetbl->remove(): Remove an object from this table with an object name.
    Definition qtreetbl.c:491
    -
    bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem)
    qtreetbl->getnext(): Get next element.
    Definition qtreetbl.c:580
    -
    void qtreetbl_set_compare(qtreetbl_t *tbl, int(*cmp)(const void *name1, size_t namesize1, const void *name2, size_t namesize2))
    qtreetbl->set_compare(): Set user comparator.
    Definition qtreetbl.c:250
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2023 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 
    +
    29 /**
    +
    30  * @file qtreetbl.c Tree Table container that implements the Left-Leaning
    +
    31  * Red-Black BST algorithm.
    +
    32  *
    +
    33  * qtreetbl implements a binary search tree that allows efficient in-order
    +
    34  * traversal with O(log n) search time.
    +
    35  *
    +
    36  * The algorithm qtreetbl specifically implements is the left-leaning red-black
    +
    37  * tree algorithm invented in 2008 by Robert Sedgewick. A left-leaning red–black
    +
    38  * tree is a type of self-balancing binary search tree and it is a variant of the
    +
    39  * red–black tree which was invented in 1972 by Rudolf Bayer.
    +
    40  *
    +
    41  * References:
    +
    42  * - http://en.wikipedia.org/wiki/Left-leaning_red-black_tree
    +
    43  * - https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf
    +
    44  *
    +
    45  * (E)
    +
    46  * ______________|______________
    +
    47  * / \
    +
    48  * (C) (R)
    +
    49  * / \ / \
    +
    50  * (A,B) (D) (I,N) (S,X)
    +
    51  *
    +
    52  * <2-3-4 Tree Data Structure>
    +
    53  *
    +
    54  * E
    +
    55  * ______________|______________
    +
    56  * / \
    +
    57  * C R
    +
    58  * _______|_______ _______|_______
    +
    59  * / \ / \
    +
    60  * B D N X
    +
    61  * // // //
    +
    62  * A* I* S*
    +
    63  *
    +
    64  * <Left-Leaning Red-Black Tree Data Structure>
    +
    65  * Nodes A, I and S are nodes with RED upper link. Others are BLACK
    +
    66  *
    +
    67  *
    +
    68  * The Red-Black BST algorithm has been one of the popular BST algorithms
    +
    69  * especially for in-memory operation. The Left-Leaning version of Red-Black
    +
    70  * especially improves performance and reduces overall complexity.
    +
    71  *
    +
    72  * Since it's relatively new algorithm, there's not many practically functional
    +
    73  * working codes yet other than proof of concept kinds. Here's one of fully
    +
    74  * functional codes and I, Seungyoung Kim, would like to dedicate this code to
    +
    75  * the genius inventor Robert Sedgewick and to all the great qLibc users.
    +
    76  *
    +
    77  * Additional features:
    +
    78  * - iterator.
    +
    79  * - find nearest key.
    +
    80  * - iteration from given key.
    +
    81  * - find min/max key.
    +
    82  *
    +
    83  * @code
    +
    84  * qtreetbl_t *tbl = qtreetbl(QTREETBL_THREADSAFE);
    +
    85  *
    +
    86  * tbl->put(tbl, "KEY", "DATA", 4); // use putobj() for binary keys.
    +
    87  * void *data = tbl->get(tbl, "KEY", false); // use getobj() for binary keys.
    +
    88  * tbl->remove(tbl, "KEY"); // use remove_by_key() for binary keys.
    +
    89  *
    +
    90  * // iteration example
    +
    91  * qtreetbl_obj_t obj;
    +
    92  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    93  * tbl->lock(tbl); // for thread safety
    +
    94  * while (tbl->getnext(tbl, &obj, false) == true) {
    +
    95  * ...
    +
    96  * }
    +
    97  * tbl->unlock(tbl);
    +
    98  *
    +
    99  * // find a nearest key then iterate from there
    +
    100  * tbl->lock(tbl);
    +
    101  * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "K", sizeof("K"), false);
    +
    102  * tbl->lock(tbl); // for thread safety
    +
    103  * while (tbl->getnext(tbl, &obj, false) == true) {
    +
    104  * ...
    +
    105  * }
    +
    106  * tbl->unlock(tbl);
    +
    107  *
    +
    108  * // find minimum value using custom comparator
    +
    109  * tbl->set_compare(tbl, my_compare_func); // default is byte comparator
    +
    110  * void *min = tbl->find_min(tbl, &keysize);
    +
    111  *
    +
    112  * // get total number of objects in the table
    +
    113  * size_t num = tbl->size(tbl);
    +
    114  *
    +
    115  * tbl->free(tbl);
    +
    116  * @endcode
    +
    117  */
    +
    118 
    +
    119 #include <stdio.h>
    +
    120 #include <stdlib.h>
    +
    121 #include <stdbool.h>
    +
    122 #include <string.h>
    +
    123 #include <stdarg.h>
    +
    124 #include <errno.h>
    +
    125 #include <assert.h>
    +
    126 #include "qinternal.h"
    +
    127 #include "utilities/qstring.h"
    +
    128 #include "containers/qtreetbl.h"
    +
    129 
    +
    130 #ifndef _DOXYGEN_SKIP
    +
    131 
    +
    132 /* internal functions */
    +
    133 #define LLRB234 // comment to build 2-3 variant
    +
    134 static bool is_red(qtreetbl_obj_t *obj);
    +
    135 static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj);
    +
    136 static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj);
    +
    137 static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj);
    +
    138 static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj);
    +
    139 static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj);
    +
    140 static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj);
    +
    141 static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj);
    +
    142 static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj);
    +
    143 static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj);
    +
    144 static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
    +
    145  size_t namesize);
    +
    146 static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
    +
    147  const void *data, size_t datasize);
    +
    148 static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    +
    149  const void *name, size_t namesize,
    +
    150  const void *data, size_t datasize);
    +
    151 static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    +
    152  const void *name, size_t namesize);
    +
    153 static void free_objs(qtreetbl_obj_t *obj);
    +
    154 static uint8_t reset_iterator(qtreetbl_t *tbl);
    +
    155 
    +
    156 struct branch_obj_s {
    +
    157  struct branch_obj_s *p;
    +
    158  char *s;
    +
    159 };
    +
    160 static void print_branch(struct branch_obj_s *branch, FILE *out);
    +
    161 static void print_node(qtreetbl_obj_t *obj, FILE *out, struct branch_obj_s *prev,
    +
    162  bool right);
    +
    163 #endif
    +
    164 
    +
    165 /**
    +
    166  * Initialize a tree table.
    +
    167  *
    +
    168  * @param options combination of initialization options.
    +
    169  *
    +
    170  * @return a pointer of malloced qtreetbl_t, otherwise returns NULL.
    +
    171  * @retval errno will be set in error condition.
    +
    172  * - ENOMEM : Memory allocation failure.
    +
    173  *
    +
    174  * @code
    +
    175  * qtreetbl_t *tbl = qtreetbl(0);
    +
    176  * @endcode
    +
    177  *
    +
    178  * @note
    +
    179  * Available options:
    +
    180  * - QTREETBL_THREADSAFE - make it thread-safe.
    +
    181  */
    +
    182 qtreetbl_t *qtreetbl(int options) {
    +
    183  qtreetbl_t *tbl = (qtreetbl_t *) calloc(1, sizeof(qtreetbl_t));
    +
    184  if (tbl == NULL)
    +
    185  goto malloc_failure;
    +
    186 
    +
    187  // handle options.
    +
    188  if (options & QTREETBL_THREADSAFE) {
    +
    189  Q_MUTEX_NEW(tbl->qmutex, true);
    +
    190  if (tbl->qmutex == NULL)
    +
    191  goto malloc_failure;
    +
    192  }
    +
    193 
    +
    194  // assign methods
    +
    195  tbl->set_compare = qtreetbl_set_compare;
    +
    196 
    +
    197  tbl->put = qtreetbl_put;
    +
    198  tbl->putstr = qtreetbl_putstr;
    +
    199  tbl->putstrf = qtreetbl_putstrf;
    +
    200  tbl->putobj = qtreetbl_putobj;
    +
    201 
    +
    202  tbl->get = qtreetbl_get;
    +
    203  tbl->getstr = qtreetbl_getstr;
    +
    204  tbl->getobj = qtreetbl_getobj;
    +
    205 
    +
    206  tbl->remove = qtreetbl_remove;
    +
    207  tbl->removeobj = qtreetbl_removeobj;
    +
    208 
    +
    209  tbl->getnext = qtreetbl_getnext;
    +
    210 
    +
    211  tbl->find_min = qtreetbl_find_min;
    +
    212  tbl->find_max = qtreetbl_find_max;
    +
    213  tbl->find_nearest = qtreetbl_find_nearest;
    +
    214 
    +
    215  tbl->size = qtreetbl_size;
    +
    216  tbl->clear = qtreetbl_clear;
    +
    217 
    +
    218  tbl->lock = qtreetbl_lock;
    +
    219  tbl->unlock = qtreetbl_unlock;
    +
    220 
    +
    221  tbl->free = qtreetbl_free;
    +
    222  tbl->debug = qtreetbl_debug;
    +
    223 
    +
    224  // Set default comparison function.
    +
    225  qtreetbl_set_compare(tbl, qtreetbl_byte_cmp);
    +
    226  reset_iterator(tbl);
    +
    227 
    +
    228  return tbl;
    +
    229 
    +
    230  malloc_failure:
    +
    231  errno = ENOMEM;
    +
    232  if (tbl != NULL) {
    +
    233  assert(tbl->qmutex == NULL);
    +
    234  qtreetbl_free(tbl);
    +
    235  }
    +
    236  return NULL;
    +
    237 }
    +
    238 
    +
    239 /**
    +
    240  * qtreetbl->set_compare(): Set user comparator.
    +
    241  *
    +
    242  * @param tbl qtreetbl_t container pointer.
    +
    243  * @param cmp a pointer to the user comparator function.
    +
    244  *
    +
    245  * @note
    +
    246  * By default, qtreetbl uses byte comparator that works for
    +
    247  * both binary type key and string type key. Please refer
    +
    248  * qtreetbl_byte_cmp() for your idea to make your own comparator.
    +
    249  */
    +
    250 void qtreetbl_set_compare(qtreetbl_t *tbl,
    +
    251  int (*cmp)(const void *name1, size_t namesize1,
    +
    252  const void *name2, size_t namesize2)) {
    +
    253  tbl->compare = cmp;
    +
    254 }
    +
    255 
    +
    256 /**
    +
    257  * qtreetbl->put(): Put an object into this table with string type key.
    +
    258  *
    +
    259  * @param tbl qtreetbl_t container pointer.
    +
    260  * @param name key name.
    +
    261  * @param data data object.
    +
    262  * @param datasize size of data object.
    +
    263  *
    +
    264  * @return true if successful, otherwise returns false.
    +
    265  * @retval errno will be set in error condition.
    +
    266  * - EINVAL : Invalid argument.
    +
    267  * - ENOMEM : Memory allocation failure.
    +
    268  */
    +
    269 bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data,
    +
    270  size_t datasize) {
    +
    271  return qtreetbl_putobj(tbl,
    +
    272  name, (name != NULL) ? (strlen(name) + 1) : 0, data, datasize);
    +
    273 }
    +
    274 
    +
    275 /**
    +
    276  * qtreetbl->putstr(): Put a string into this table.
    +
    277  *
    +
    278  * @param tbl qtreetbl container pointer.
    +
    279  * @param name key name.
    +
    280  * @param str string data.
    +
    281  *
    +
    282  * @return true if successful, otherwise returns false.
    +
    283  * @retval errno will be set in error condition.
    +
    284  * - EINVAL : Invalid argument.
    +
    285  * - ENOMEM : Memory allocation failure.
    +
    286  */
    +
    287 bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str) {
    +
    288  return qtreetbl_putobj(tbl,
    +
    289  name, (name != NULL) ? (strlen(name) + 1) : 0,
    +
    290  str, (str != NULL) ? (strlen(str) + 1) : 0);
    +
    291 }
    +
    292 
    +
    293 /**
    +
    294  * qtreetbl->putstrf(): Put a formatted string into this table.
    +
    295  *
    +
    296  * @param tbl qtreetbl_t container pointer.
    +
    297  * @param name key name.
    +
    298  * @param format formatted string data.
    +
    299  *
    +
    300  * @return true if successful, otherwise returns false.
    +
    301  * @retval errno will be set in error condition.
    +
    302  * - EINVAL : Invalid argument.
    +
    303  * - ENOMEM : Memory allocation failure.
    +
    304  */
    +
    305 bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,
    +
    306  ...) {
    +
    307  char *str;
    +
    308  DYNAMIC_VSPRINTF(str, format);
    +
    309  if (str == NULL) {
    +
    310  errno = ENOMEM;
    +
    311  return false;
    +
    312  }
    +
    313 
    +
    314  bool ret = qtreetbl_putstr(tbl, name, str);
    +
    315  free(str);
    +
    316  return ret;
    +
    317 }
    +
    318 
    +
    319 /**
    +
    320  * qtreetbl->putobj(): Put an object data into this table with an object key.
    +
    321  *
    +
    322  * @param tbl qtreetbl_t container pointer.
    +
    323  * @param name key name.
    +
    324  * @param namesize key size.
    +
    325  * @param data data object.
    +
    326  * @param datasize size of data object.
    +
    327  *
    +
    328  * @return true if successful, otherwise returns false.
    +
    329  * @retval errno will be set in error condition.
    +
    330  * - EINVAL : Invalid argument.
    +
    331  * - ENOMEM : Memory allocation failure.
    +
    332  *
    +
    333  * @note
    +
    334  * This is the underlying put function which all other put methods use.
    +
    335  */
    +
    336 bool qtreetbl_putobj(qtreetbl_t *tbl, const void *name, size_t namesize,
    +
    337  const void *data, size_t datasize) {
    +
    338  if (name == NULL || namesize == 0) {
    +
    339  errno = EINVAL;
    +
    340  return false;
    +
    341  }
    +
    342 
    +
    343  qtreetbl_lock(tbl);
    +
    344  errno = 0;
    +
    345  qtreetbl_obj_t *root = put_obj(tbl, tbl->root, name, namesize, data,
    +
    346  datasize);
    +
    347  if (root == NULL || errno == ENOMEM) {
    +
    348  qtreetbl_unlock(tbl);
    +
    349  return false;
    +
    350  }
    +
    351  root->red = false;
    +
    352  tbl->root = root;
    +
    353  qtreetbl_unlock(tbl);
    +
    354 
    +
    355  return true;
    +
    356 }
    +
    357 
    +
    358 /**
    +
    359  * qtreetbl->get(): Get an object from this table.
    +
    360  *
    +
    361  * @param tbl qtreetbl_t container pointer.
    +
    362  * @param name key name.
    +
    363  * @param datasize if not NULL, object size will be stored.
    +
    364  * @param newmem whether or not to allocate memory for the data.
    +
    365  *
    +
    366  * @return a pointer of data if the key is found, otherwise returns NULL.
    +
    367  * @retval errno will be set in error condition.
    +
    368  * - ENOENT : No such key found.
    +
    369  * - EINVAL : Invalid argument.
    +
    370  * - ENOMEM : Memory allocation failure.
    +
    371  *
    +
    372  * @code
    +
    373  * qtreetbl_t *tbl = qtreetbl(0);
    +
    374  * (...codes...)
    +
    375  *
    +
    376  * // with newmem flag unset
    +
    377  * size_t size;
    +
    378  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, false);
    +
    379  *
    +
    380  * // with newmem flag set
    +
    381  * size_t size;
    +
    382  * void *data = (struct myobj*)tbl->get(tbl, "key_name", &size, true);
    +
    383  * free(data);
    +
    384  * @endcode
    +
    385  *
    +
    386  * @note
    +
    387  * If newmem flag is set, returned data will be malloced and should be
    +
    388  * deallocated by user. Otherwise returned pointer will point internal buffer
    +
    389  * directly and should not be de-allocated by user. In thread-safe mode,
    +
    390  * newmem flag must be set to true always.
    +
    391  */
    +
    392 void *qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize,
    +
    393  bool newmem) {
    +
    394  return qtreetbl_getobj(tbl,
    +
    395  name, (name != NULL) ? (strlen(name) + 1) : 0, datasize, newmem);
    +
    396 }
    +
    397 
    +
    398 /**
    +
    399  * qtreetbl->getstr(): Finds an object and returns it as string type.
    +
    400  *
    +
    401  * @param tbl qtreetbl_t container pointer.
    +
    402  * @param name key name.
    +
    403  * @param newmem whether or not to allocate memory for the data.
    +
    404  *
    +
    405  * @return a pointer to data if the key is found, otherwise returns NULL.
    +
    406  * @retval errno will be set in error condition.
    +
    407  * - ENOENT : No such key found.
    +
    408  * - EINVAL : Invalid argument.
    +
    409  * - ENOMEM : Memory allocation failure.
    +
    410  *
    +
    411  * @note
    +
    412  * If newmem flag is set, returned data will be malloced and should be
    +
    413  * deallocated by user. Otherwise returned pointer will point internal buffer
    +
    414  * directly and should not be de-allocated by user. In thread-safe mode,
    +
    415  * newmem flag must be set to true always.
    +
    416  */
    +
    417 char *qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem) {
    +
    418  return qtreetbl_getobj(tbl,
    +
    419  name, (name != NULL) ? (strlen(name) + 1) : 0, NULL, newmem);
    +
    420 }
    +
    421 
    +
    422 /**
    +
    423  * qtreetbl->getobj(): Get an object from this table with an object name.
    +
    424  *
    +
    425  * @param tbl qtreetbl_t container pointer.
    +
    426  * @param name key name.
    +
    427  * @param namesize key size.
    +
    428  * @param datasize if not NULL, oject size will be stored.
    +
    429  * @param newmem whether or not to allocate memory for the data.
    +
    430  *
    +
    431  * @return a pointer of data if the key is found, otherwise returns NULL.
    +
    432  * @retval errno will be set in error condition.
    +
    433  * - ENOENT : No such key found.
    +
    434  * - EINVAL : Invalid argument.
    +
    435  * - ENOMEM : Memory allocation failure.
    +
    436  *
    +
    437  * @note
    +
    438  * If newmem flag is set, returned data will be malloced and should be
    +
    439  * deallocated by user. Otherwise returned pointer will point internal buffer
    +
    440  * directly and should not be de-allocated by user. In thread-safe mode,
    +
    441  * newmem flag must be set to true always.
    +
    442  */
    +
    443 void *qtreetbl_getobj(qtreetbl_t *tbl, const void *name, size_t namesize,
    +
    444  size_t *datasize, bool newmem) {
    +
    445  if (name == NULL || namesize == 0) {
    +
    446  errno = EINVAL;
    +
    447  return NULL;
    +
    448  }
    +
    449 
    +
    450  qtreetbl_lock(tbl);
    +
    451  qtreetbl_obj_t *obj = find_obj(tbl, name, namesize);
    +
    452  void *data = NULL;
    +
    453  if (obj != NULL) {
    +
    454  data = (newmem) ? qmemdup(obj->data, obj->datasize) : obj->data;
    +
    455  if (datasize != NULL) {
    +
    456  *datasize = (data != NULL) ? obj->datasize : 0;
    +
    457  }
    +
    458  }
    +
    459  qtreetbl_unlock(tbl);
    +
    460  return data;
    +
    461 }
    +
    462 
    +
    463 /**
    +
    464  * qtreetbl->remove(): Remove an object from this table.
    +
    465  *
    +
    466  * @param tbl qtreetbl_t container pointer.
    +
    467  * @param name key name.
    +
    468  *
    +
    469  * @return true if successful, otherwise(not found) returns false.
    +
    470  * @retval errno will be set in error condition.
    +
    471  * - ENOENT : No such key found.
    +
    472  * - EINVAL : Invalid argument.
    +
    473  */
    +
    474 bool qtreetbl_remove(qtreetbl_t *tbl, const char *name) {
    +
    475  return qtreetbl_removeobj(tbl,
    +
    476  name, (name != NULL) ? strlen(name) + 1 : 0);
    +
    477 }
    +
    478 
    +
    479 /**
    +
    480  * qtreetbl->remove(): Remove an object from this table with an object name.
    +
    481  *
    +
    482  * @param tbl qtreetbl_t container pointer.
    +
    483  * @param name key name.
    +
    484  * @param name key size.
    +
    485  *
    +
    486  * @return true if successful, otherwise(not found) returns false.
    +
    487  * @retval errno will be set in error condition.
    +
    488  * - ENOENT : No such key found.
    +
    489  * - EINVAL : Invalid argument.
    +
    490  */
    +
    491 bool qtreetbl_removeobj(qtreetbl_t *tbl, const void *name, size_t namesize) {
    +
    492  if (name == NULL) {
    +
    493  errno = EINVAL;
    +
    494  return false;
    +
    495  }
    +
    496 
    +
    497  qtreetbl_lock(tbl);
    +
    498  errno = 0;
    +
    499  tbl->root = remove_obj(tbl, tbl->root, name, namesize);
    +
    500  if (tbl->root != NULL) {
    +
    501  tbl->root->red = false;
    +
    502  }
    +
    503  bool removed = (errno != ENOENT) ? true : false;
    +
    504  qtreetbl_unlock(tbl);
    +
    505 
    +
    506  return removed;
    +
    507 }
    +
    508 
    +
    509 /**
    +
    510  * qtreetbl->getnext(): Get next element.
    +
    511  *
    +
    512  * @param tbl qtreetbl_t container pointer.
    +
    513  * @param obj found data will be stored in this object.
    +
    514  * @param newmem whether or not to allocate memory for the data.
    +
    515  *
    +
    516  * @return true if found otherwise returns false.
    +
    517  * @retval errno will be set in error condition.
    +
    518  * - ENOENT : No next element.
    +
    519  * - EINVAL : Invalid argument.
    +
    520  * - ENOMEM : Memory allocation failure.
    +
    521  *
    +
    522  * @code
    +
    523  * [Iteration example from the beginning]
    +
    524  *
    +
    525  * qtreetbl_obj_t obj;
    +
    526  * memset((void*)&obj, 0, sizeof(obj)); // must be cleared before call
    +
    527  * tbl->lock(tbl); // lock it when thread condition is expected
    +
    528  * while(tbl->getnext(tbl, &obj, false) == true) {
    +
    529  * //
    +
    530  * // obj.name : key data
    +
    531  * // obj.namesize : key size
    +
    532  * // obj.data : data
    +
    533  * // obj.datasize : data size
    +
    534  * //
    +
    535  * // Do free obj.name and obj.data if newmem is set to true;
    +
    536  * }
    +
    537  * tbl->unlock(tbl);
    +
    538  * @endcode
    +
    539  *
    +
    540  * @code
    +
    541  * [Iteration example from given point]
    +
    542  *
    +
    543  * tbl->lock(tbl); // to guarantee no table update during the run
    +
    544  * qtreetbl_obj_t obj = tbl->find_nearest(tbl, "F", sizeof("F"), false);
    +
    545  * while (tbl->getnext(tbl, &obj, false) == true) { // newmem is false
    +
    546  * // If a tree has 5 objects, A, C, E, G and I.
    +
    547  * // The iteration sequence from nearest "F" will be: E->G->I->A->C
    +
    548  * }
    +
    549  * tbl->unlock(tbl);
    +
    550  *
    +
    551  * @endcode
    +
    552  *
    +
    553  * @code
    +
    554  * [Removal example in iteration loop]
    +
    555  *
    +
    556  * qtreetbl_obj_t obj;
    +
    557  * memset((void*)&obj, 0, sizeof(obj));
    +
    558  * tbl->lock(tbl);
    +
    559  * while (tbl->getnext(tbl, &obj, false) == true) {
    +
    560  * if (...condition...) {
    +
    561  * char *name = qmemdup(obj.name, obj.namesize); // keep the name
    +
    562  * size_t namesize = obj.namesize; // for removal argument
    +
    563  * tbl->removeobj(tbl, obj.name, obj.namesize); // remove
    +
    564  * obj = tbl->find_nearest(tbl, name, namesize, false); // rewind one step back
    +
    565  * free(name); // clean up
    +
    566  * }
    +
    567  * }
    +
    568  * tbl->unlock(tbl);
    +
    569  * @endcode
    +
    570  *
    +
    571  * @note
    +
    572  * - Data insertion or deletion can be made during the traversal, but in that
    +
    573  * case iterator doesn't guarantee full sweep and possibly skip some visits.
    +
    574  * When deletion happens in getnext() loop, use find_nearest() to rewind the
    +
    575  * iterator one step back.
    +
    576  * - Object obj should be initialized with 0 by using memset() before first call.
    +
    577  * - If newmem flag is true, user should de-allocate obj.name and obj.data
    +
    578  * resources.
    +
    579  */
    +
    580 bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem) {
    +
    581  if (obj == NULL) {
    +
    582  errno = EINVAL;
    +
    583  return NULL;
    +
    584  }
    +
    585 
    +
    586  uint8_t tid = obj->tid;
    +
    587  if (obj->next == NULL) { // first time call
    +
    588  if (tbl->root == NULL) {
    +
    589  return false;
    +
    590  }
    +
    591  // get a new iterator id
    +
    592  tid = reset_iterator(tbl);;
    +
    593  }
    +
    594 
    +
    595  qtreetbl_obj_t *cursor = ((obj->next != NULL) ? obj->next : tbl->root);
    +
    596  while (cursor != NULL) {
    +
    597  if (cursor->left != NULL && cursor->left->tid != tid) {
    +
    598  cursor->left->next = cursor;
    +
    599  cursor = cursor->left;
    +
    600  continue;
    +
    601  } else if (cursor->tid != tid) {
    +
    602  cursor->tid = tid;
    +
    603  *obj = *cursor;
    +
    604  if (newmem) {
    +
    605  obj->name = qmemdup(cursor->name, cursor->namesize);
    +
    606  obj->data = qmemdup(cursor->data, cursor->datasize);
    +
    607  }
    +
    608  obj->next = cursor; // store original address in tree for next iteration
    +
    609  return true;
    +
    610  } else if (cursor->right != NULL && cursor->right->tid != tid) {
    +
    611  cursor->right->next = cursor;
    +
    612  cursor = cursor->right;
    +
    613  continue;
    +
    614  }
    +
    615  cursor = cursor->next;
    +
    616  }
    +
    617 
    +
    618  // end of travel, reset iterator to allow iteration start over in next call
    +
    619  reset_iterator(tbl);
    +
    620  return false;
    +
    621 }
    +
    622 
    +
    623 /**
    +
    624  * qtreetbl->find_min(): Find the name of the leftmost object.
    +
    625  *
    +
    626  * @param tbl qtreetbl_t container pointer.
    +
    627  * @param namesize if not NULL, the size of key name will be stored.
    +
    628  *
    +
    629  * @return malloced memory copying the key name.
    +
    630  *
    +
    631  * @note
    +
    632  * It's user's responsibility to free the return.
    +
    633  */
    +
    634 void *qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize) {
    +
    635  qtreetbl_lock(tbl);
    +
    636  qtreetbl_obj_t *obj = find_min(tbl->root);
    +
    637  if (obj == NULL) {
    +
    638  errno = ENOENT;
    +
    639  qtreetbl_unlock(tbl);
    +
    640  return NULL;
    +
    641  }
    +
    642 
    +
    643  if (namesize != NULL) {
    +
    644  *namesize = obj->namesize;
    +
    645  }
    +
    646  void *name = qmemdup(obj->name, obj->namesize);
    +
    647  qtreetbl_unlock(tbl);
    +
    648  return name;
    +
    649 }
    +
    650 
    +
    651 /**
    +
    652  * qtreetbl->find_max(): Find the name of the rightmost object.
    +
    653  *
    +
    654  * @param tbl qtreetbl_t container pointer.
    +
    655  * @param namesize if not NULL, the size of key name will be stored.
    +
    656  *
    +
    657  * @return malloced memory copying the key name.
    +
    658  *
    +
    659  * @note
    +
    660  * It's user's responsibility to free the return.
    +
    661  */
    +
    662 void *qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize) {
    +
    663  qtreetbl_lock(tbl);
    +
    664  qtreetbl_obj_t *obj = find_max(tbl->root);
    +
    665  if (obj == NULL) {
    +
    666  errno = ENOENT;
    +
    667  qtreetbl_unlock(tbl);
    +
    668  return NULL;
    +
    669  }
    +
    670 
    +
    671  if (namesize != NULL) {
    +
    672  *namesize = obj->namesize;
    +
    673  }
    +
    674  void *name = qmemdup(obj->name, obj->namesize);
    +
    675  qtreetbl_unlock(tbl);
    +
    676  return name;
    +
    677 }
    +
    678 
    +
    679 /**
    +
    680  * qtreetbl->find_nearest(): Find an object with matching key or nearest.
    +
    681  *
    +
    682  * find_nearest() returns matching key or nearest key object. If there's
    +
    683  * no keys in the table. It'll return empty qtreetbl_obj_t object
    +
    684  * with errno ENOENT.
    +
    685  *
    +
    686  * @param tbl qtreetbl_t container pointer.
    +
    687  * @param name key name.
    +
    688  * @param namesize key size.
    +
    689  * @param newmem whether or not to allocate memory for the data.
    +
    690  *
    +
    691  * @return qtreetbl_obj_t object.
    +
    692  *
    +
    693  * @retval errno will be set in error condition.
    +
    694  * - ENOENT : No next element.
    +
    695  * - EINVAL : Invalid argument.
    +
    696  * - ENOMEM : Memory allocation failure.
    +
    697  *
    +
    698  * @code
    +
    699  * Data Set : A B C D E I N R S X
    +
    700  * find_nearest("0") => "A" // no smaller key available, so "A"
    +
    701  * find_nearest("C") => "C" // matching key found
    +
    702  * find_nearest("F") => "E" // "E" is nearest smaller key from "F"
    +
    703  * @endcode
    +
    704  *
    +
    705  * @note
    +
    706  * When there's no matching key it look for closest smaller key
    +
    707  * in the neighbors. The only exception when it returns bigger key
    +
    708  * than given search key is that when there's no smaller keys available
    +
    709  * in the table. In such case, it'll return the nearest bigger key.
    +
    710  */
    +
    711 qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name,
    +
    712  size_t namesize, bool newmem) {
    +
    713  qtreetbl_obj_t retobj;
    +
    714  memset((void*) &retobj, 0, sizeof(retobj));
    +
    715 
    +
    716  if (name == NULL || namesize == 0) {
    +
    717  errno = EINVAL;
    +
    718  return retobj;
    +
    719  }
    +
    720 
    +
    721  qtreetbl_lock(tbl);
    +
    722  qtreetbl_obj_t *obj, *lastobj;
    +
    723  for (obj = lastobj = tbl->root; obj != NULL;) {
    +
    724  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    725  if (cmp == 0) {
    +
    726  break;
    +
    727  }
    +
    728  lastobj = obj;
    +
    729  if (cmp < 0) {
    +
    730  if (obj->left != NULL) {
    +
    731  obj->left->next = obj;
    +
    732  }
    +
    733  obj = obj->left;
    +
    734  } else {
    +
    735  if (obj->right != NULL) {
    +
    736  obj->right->next = obj;
    +
    737  }
    +
    738  obj = obj->right;
    +
    739  }
    +
    740  }
    +
    741 
    +
    742  if (obj == NULL) {
    +
    743  for (obj = lastobj;
    +
    744  obj != NULL && (tbl->compare(name, namesize, obj->name, obj->namesize) < 0);
    +
    745  obj = obj->next);
    +
    746  if (obj == NULL) {
    +
    747  obj = lastobj;
    +
    748  }
    +
    749  }
    +
    750 
    +
    751  if (obj != NULL) {
    +
    752  retobj = *obj;
    +
    753  if (newmem) {
    +
    754  retobj.name = qmemdup(obj->name, obj->namesize);
    +
    755  retobj.data = qmemdup(obj->data, obj->datasize);
    +
    756  }
    +
    757  // set travel info to be used for iteration in getnext()
    +
    758  retobj.tid = tbl->tid;
    +
    759  retobj.next = obj;
    +
    760  } else {
    +
    761  errno = ENOENT;
    +
    762  }
    +
    763 
    +
    764  qtreetbl_unlock(tbl);
    +
    765  return retobj;
    +
    766 }
    +
    767 
    +
    768 /**
    +
    769  * qtreetbl->size(): Returns the number of keys in the table.
    +
    770  *
    +
    771  * @param tbl qtreetbl_t container pointer.
    +
    772  *
    +
    773  * @return number of elements stored.
    +
    774  */
    +
    775 size_t qtreetbl_size(qtreetbl_t *tbl) {
    +
    776  return tbl->num;
    +
    777 }
    +
    778 
    +
    779 /**
    +
    780  * qtreetbl->clear(): Clears the table so that it contains no keys.
    +
    781  *
    +
    782  * @param tbl qtreetbl_t container pointer.
    +
    783  */
    +
    784 void qtreetbl_clear(qtreetbl_t *tbl) {
    +
    785  qtreetbl_lock(tbl);
    +
    786  free_objs(tbl->root);
    +
    787  tbl->root = NULL;
    +
    788  tbl->num = 0;
    +
    789  qtreetbl_unlock(tbl);
    +
    790 }
    +
    791 
    +
    792 /**
    +
    793  * qtreetbl->lock(): Enter critical section.
    +
    794  *
    +
    795  * @param tbl qtreetbl_t container pointer.
    +
    796  *
    +
    797  * @note
    +
    798  * From user side, normally locking operation is only needed when traverse
    +
    799  * all elements using qtreetbl->getnext().
    +
    800  *
    +
    801  * @note
    +
    802  * This operation will do nothing if QTREETBL_THREADSAFE option was not
    +
    803  * given at the initialization time.
    +
    804  */
    +
    805 void qtreetbl_lock(qtreetbl_t *tbl) {
    +
    806  Q_MUTEX_ENTER(tbl->qmutex);
    +
    807 }
    +
    808 
    +
    809 /**
    +
    810  * qtreetbl->unlock(): Leave critical section.
    +
    811  *
    +
    812  * @param tbl qtreetbl_t container pointer.
    +
    813  *
    +
    814  * @note
    +
    815  * This operation will do nothing if QTREETBL_THREADSAFE option was not
    +
    816  * given at the initialization time.
    +
    817  */
    +
    818 void qtreetbl_unlock(qtreetbl_t *tbl) {
    +
    819  Q_MUTEX_LEAVE(tbl->qmutex);
    +
    820 }
    +
    821 
    +
    822 /**
    +
    823  * qtreetbl->free(): De-allocate the table.
    +
    824  *
    +
    825  * @param tbl qtreetbl_t container pointer.
    +
    826  */
    +
    827 void qtreetbl_free(qtreetbl_t *tbl) {
    +
    828  qtreetbl_clear(tbl);
    +
    829  Q_MUTEX_DESTROY(tbl->qmutex);
    +
    830  free(tbl);
    +
    831 }
    +
    832 
    +
    833 int qtreetbl_byte_cmp(const void *name1, size_t namesize1, const void *name2,
    +
    834  size_t namesize2) {
    +
    835  size_t minsize = (namesize1 < namesize2) ? namesize1 : namesize2;
    +
    836  int cmp = memcmp(name1, name2, minsize);
    +
    837  if (cmp != 0 || namesize1 == namesize2) {
    +
    838  return cmp;
    +
    839  }
    +
    840  return (namesize1 < namesize2) ? -1 : +1;
    +
    841 }
    +
    842 
    +
    843 /**
    +
    844  * qtreetbl->debug(): Print the internal tree structure in text.
    +
    845  *
    +
    846  * @param tbl qtreetbl_t container pointer.
    +
    847  * @param out output stream.
    +
    848  *
    +
    849  * @return true if successful, otherwise returns false.
    +
    850  * @retval errno will be set in error condition.
    +
    851  * - EIO : Invalid output stream.
    +
    852  *
    +
    853  * @code
    +
    854  * Example output:
    +
    855  *
    +
    856  * ┌───9
    +
    857  * │ └──[8]
    +
    858  * ┌───7
    +
    859  * │ │ ┌───6
    +
    860  * │ └──[5]
    +
    861  * │ └───4
    +
    862  * 3
    +
    863  * │ ┌───2
    +
    864  * └───1
    +
    865  * └───0
    +
    866  * @endcode
    +
    867  * @note
    +
    868  * Red nodes are wrapped in `[]`.
    +
    869  * In this example, 5 and 8 are Red nodes.
    +
    870  */
    +
    871 bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out) {
    +
    872  if (out == NULL) {
    +
    873  errno = EIO;
    +
    874  return false;
    +
    875  }
    +
    876 
    +
    877  qtreetbl_lock(tbl);
    +
    878  print_node(tbl->root, out, NULL, false);
    +
    879  qtreetbl_unlock(tbl);
    +
    880  return true;
    +
    881 }
    +
    882 
    +
    883 /**
    +
    884  * Verifies that root property of the red-black tree is satisfied.
    +
    885  *
    +
    886  * Root property: The root is black.
    +
    887  *
    +
    888  * @param tbl qtreetbl_t container pointer.
    +
    889  */
    +
    890 int node_check_root(qtreetbl_t *tbl) {
    +
    891  if (tbl == NULL) {
    +
    892  return 1;
    +
    893  }
    +
    894 
    +
    895  if (is_red(tbl->root)) {
    +
    896  return 1;
    +
    897  }
    +
    898  return 0;
    +
    899 }
    +
    900 
    +
    901 /**
    +
    902  * Verifies that red property of the red-black tree is satisfied.
    +
    903  *
    +
    904  * Red property: If a node is red, then both its children are black.
    +
    905  *
    +
    906  * @param tbl qtreetbl_t container pointer.
    +
    907  * @param obj qtreetbl_obj_t object pointer.
    +
    908  */
    +
    909 int node_check_red(qtreetbl_t *tbl, qtreetbl_obj_t *obj) {
    +
    910  if (obj == NULL) {
    +
    911  return 0;
    +
    912  }
    +
    913 
    +
    914  if (is_red(obj)) {
    +
    915  if (is_red(obj->right) || is_red(obj->left)) {
    +
    916  return 1;
    +
    917  }
    +
    918  }
    +
    919 
    +
    920  if (node_check_red(tbl, obj->right)) {
    +
    921  return 1;
    +
    922  }
    +
    923  if (node_check_red(tbl, obj->left)) {
    +
    924  return 1;
    +
    925  }
    +
    926 
    +
    927  return 0;
    +
    928 }
    +
    929 
    +
    930 /**
    +
    931  * Verifies that black property of the red-black tree is satisfied.
    +
    932  *
    +
    933  * Black property: For each node, all simple paths from the node to
    +
    934  * descendant leaves contain the same number of black nodes.
    +
    935  *
    +
    936  * @param tbl qtreetbl_t container pointer.
    +
    937  * @param obj qtreetbl_obj_t object pointer.
    +
    938  * @param path_len black path length.
    +
    939  */
    +
    940 int node_check_black(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len) {
    +
    941  if (obj == NULL) {
    +
    942  *path_len = 1;
    +
    943  return 0;
    +
    944  }
    +
    945 
    +
    946  int right_path_len;
    +
    947  if (node_check_black(tbl, obj->right, &right_path_len)) {
    +
    948  return 1;
    +
    949  }
    +
    950  int left_path_len;
    +
    951  if (node_check_black(tbl, obj->left, &left_path_len)) {
    +
    952  return 1;
    +
    953  }
    +
    954 
    +
    955  if (right_path_len != left_path_len) {
    +
    956  return 1;
    +
    957  }
    +
    958  *path_len = (!is_red(obj)) ? (right_path_len + 1) : right_path_len;
    +
    959 
    +
    960  return 0;
    +
    961 }
    +
    962 
    +
    963 /**
    +
    964  * Verifies that LLRB property of the left-leaning red-black tree is satisfied.
    +
    965  *
    +
    966  * LLRB property: 3-nodes always lean to the left and 4-nodes are balanced.
    +
    967  *
    +
    968  * @param tbl qtreetbl_t container pointer.
    +
    969  * @param obj qtreetbl_obj_t object pointer.
    +
    970  */
    +
    971 int node_check_llrb(qtreetbl_t *tbl, qtreetbl_obj_t *obj) {
    +
    972  if (obj == NULL) {
    +
    973  return 0;
    +
    974  }
    +
    975 
    +
    976  if (is_red(obj->right) && !is_red(obj->left)) {
    +
    977  return 1;
    +
    978  }
    +
    979 
    +
    980  if (node_check_llrb(tbl, obj->right)) {
    +
    981  return 1;
    +
    982  }
    +
    983  if (node_check_llrb(tbl, obj->left)) {
    +
    984  return 1;
    +
    985  }
    +
    986 
    +
    987  return 0;
    +
    988 }
    +
    989 
    +
    990 /**
    +
    991  * Verifies that the invariants of the red-black tree are satisfied.
    +
    992  *
    +
    993  * Root property: The root is black.
    +
    994  * Red property: If a node is red, then both its children are black.
    +
    995  * Black property: For each node, all simple paths from the node to
    +
    996  * descendant leaves contain the same number of black nodes.
    +
    997  * LLRB property: 3-nodes always lean to the left and 4-nodes are balanced.
    +
    998  *
    +
    999  * @param tbl qtreetbl_t container pointer.
    +
    1000  */
    +
    1001 int qtreetbl_check(qtreetbl_t *tbl) {
    +
    1002  if (tbl == NULL) {
    +
    1003  return 0;
    +
    1004  }
    +
    1005 
    +
    1006  if (node_check_root(tbl)) {
    +
    1007  return 1;
    +
    1008  }
    +
    1009  if (node_check_red(tbl, tbl->root)) {
    +
    1010  return 2;
    +
    1011  }
    +
    1012  int path_len = 0;
    +
    1013  if (node_check_black(tbl, tbl->root, &path_len)) {
    +
    1014  return 3;
    +
    1015  }
    +
    1016  if (node_check_llrb(tbl, tbl->root)) {
    +
    1017  return 4;
    +
    1018  }
    +
    1019 
    +
    1020  return 0;
    +
    1021 }
    +
    1022 
    +
    1023 #ifndef _DOXYGEN_SKIP
    +
    1024 
    +
    1025 static bool is_red(qtreetbl_obj_t *obj) {
    +
    1026  return (obj != NULL) ? obj->red : false;
    +
    1027 }
    +
    1028 
    +
    1029 uint32_t _q_treetbl_flip_color_cnt = 0;
    +
    1030 static qtreetbl_obj_t *flip_color(qtreetbl_obj_t *obj) {
    +
    1031  obj->red = !(obj->red);
    +
    1032  obj->left->red = !(obj->left->red);
    +
    1033  obj->right->red = !(obj->right->red);
    +
    1034  _q_treetbl_flip_color_cnt++;
    +
    1035  return obj;
    +
    1036 }
    +
    1037 
    +
    1038 uint32_t _q_treetbl_rotate_left_cnt = 0;
    +
    1039 static qtreetbl_obj_t *rotate_left(qtreetbl_obj_t *obj) {
    +
    1040  qtreetbl_obj_t *x = obj->right;
    +
    1041  obj->right = x->left;
    +
    1042  x->left = obj;
    +
    1043  x->red = x->left->red;
    +
    1044  x->left->red = true;
    +
    1045  _q_treetbl_rotate_left_cnt++;
    +
    1046  return x;
    +
    1047 }
    +
    1048 
    +
    1049 uint32_t _q_treetbl_rotate_right_cnt = 0;
    +
    1050 static qtreetbl_obj_t *rotate_right(qtreetbl_obj_t *obj) {
    +
    1051  qtreetbl_obj_t *x = obj->left;
    +
    1052  obj->left = x->right;
    +
    1053  x->right = obj;
    +
    1054  x->red = x->right->red;
    +
    1055  x->right->red = true;
    +
    1056  _q_treetbl_rotate_right_cnt++;
    +
    1057  return x;
    +
    1058 }
    +
    1059 
    +
    1060 static qtreetbl_obj_t *move_red_left(qtreetbl_obj_t *obj) {
    +
    1061  flip_color(obj);
    +
    1062  if (is_red(obj->right->left)) {
    +
    1063  obj->right = rotate_right(obj->right);
    +
    1064  obj = rotate_left(obj);
    +
    1065  flip_color(obj);
    +
    1066 #ifdef LLRB234
    +
    1067  // 2-3-4 exclusive
    +
    1068  if (is_red(obj->right->right)) {
    +
    1069  obj->right = rotate_left(obj->right);
    +
    1070  }
    +
    1071 #endif
    +
    1072  }
    +
    1073  return obj;
    +
    1074 }
    +
    1075 
    +
    1076 static qtreetbl_obj_t *move_red_right(qtreetbl_obj_t *obj) {
    +
    1077  flip_color(obj);
    +
    1078  if (is_red(obj->left->left)) {
    +
    1079  obj = rotate_right(obj);
    +
    1080  flip_color(obj);
    +
    1081  }
    +
    1082  return obj;
    +
    1083 }
    +
    1084 
    +
    1085 static qtreetbl_obj_t *find_min(qtreetbl_obj_t *obj) {
    +
    1086  if (obj == NULL) {
    +
    1087  errno = ENOENT;
    +
    1088  return NULL;
    +
    1089  }
    +
    1090 
    +
    1091  for (; obj->left != NULL; obj = obj->left);
    +
    1092  return obj;
    +
    1093 }
    +
    1094 
    +
    1095 static qtreetbl_obj_t *find_max(qtreetbl_obj_t *obj) {
    +
    1096  if (obj == NULL) {
    +
    1097  errno = ENOENT;
    +
    1098  return NULL;
    +
    1099  }
    +
    1100 
    +
    1101  for (; obj->right != NULL; obj = obj->right);
    +
    1102  return obj;
    +
    1103 }
    +
    1104 
    +
    1105 static qtreetbl_obj_t *remove_min(qtreetbl_obj_t *obj) {
    +
    1106  if (obj->left == NULL) {
    +
    1107  // 3-nodes are left-leaning, so this is a leaf.
    +
    1108  free(obj->name);
    +
    1109  free(obj->data);
    +
    1110  return NULL;
    +
    1111  }
    +
    1112  if (!is_red(obj->left) && !is_red(obj->left->left)) {
    +
    1113  obj = move_red_left(obj);
    +
    1114  }
    +
    1115  obj->left = remove_min(obj->left);
    +
    1116  return fix(obj);
    +
    1117 }
    +
    1118 
    +
    1119 static qtreetbl_obj_t *fix(qtreetbl_obj_t *obj) {
    +
    1120  // rotate right red to left
    +
    1121  if (is_red(obj->right)) {
    +
    1122 #ifdef LLRB234
    +
    1123  // 2-3-4 exclusive
    +
    1124  if (is_red(obj->right->left)) {
    +
    1125  obj->right = rotate_right(obj->right);
    +
    1126  }
    +
    1127 #endif
    +
    1128  obj = rotate_left(obj);
    +
    1129  }
    +
    1130  // rotate left red-red to right
    +
    1131  if (is_red(obj->left) && is_red(obj->left->left)) {
    +
    1132  obj = rotate_right(obj);
    +
    1133  }
    +
    1134 #ifndef LLRB234
    +
    1135  // split 4-nodes (2-3 exclusive)
    +
    1136  if (is_red(obj->left) && is_red(obj->right)) {
    +
    1137  flip_color(obj);
    +
    1138  }
    +
    1139 #endif
    +
    1140  return obj;
    +
    1141 }
    +
    1142 
    +
    1143 static qtreetbl_obj_t *find_obj(qtreetbl_t *tbl, const void *name,
    +
    1144  size_t namesize) {
    +
    1145  if (name == NULL || namesize == 0) {
    +
    1146  errno = EINVAL;
    +
    1147  return NULL;
    +
    1148  }
    +
    1149 
    +
    1150  qtreetbl_obj_t *obj;
    +
    1151  for (obj = tbl->root; obj != NULL;) {
    +
    1152  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    1153  if (cmp == 0) {
    +
    1154  return obj;
    +
    1155  }
    +
    1156  obj = (cmp < 0) ? obj->left : obj->right;
    +
    1157  }
    +
    1158 
    +
    1159  errno = ENOENT;
    +
    1160  return NULL;
    +
    1161 }
    +
    1162 
    +
    1163 static qtreetbl_obj_t *new_obj(bool red, const void *name, size_t namesize,
    +
    1164  const void *data, size_t datasize) {
    +
    1165  qtreetbl_obj_t *obj = (qtreetbl_obj_t *) calloc(1, sizeof(qtreetbl_obj_t));
    +
    1166  void *copyname = qmemdup(name, namesize);
    +
    1167  void *copydata = qmemdup(data, datasize);
    +
    1168 
    +
    1169  if (obj == NULL || copyname == NULL) {
    +
    1170  errno = ENOMEM;
    +
    1171  free(obj);
    +
    1172  free(copyname);
    +
    1173  free(copydata);
    +
    1174  return NULL;
    +
    1175  }
    +
    1176 
    +
    1177  obj->red = red;
    +
    1178  obj->name = copyname;
    +
    1179  obj->namesize = namesize;
    +
    1180  obj->data = copydata;
    +
    1181  obj->datasize = datasize;
    +
    1182 
    +
    1183  return obj;
    +
    1184 }
    +
    1185 
    +
    1186 static qtreetbl_obj_t *put_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    +
    1187  const void *name, size_t namesize,
    +
    1188  const void *data, size_t datasize) {
    +
    1189  if (obj == NULL) {
    +
    1190  tbl->num++;
    +
    1191  return new_obj(true, name, namesize, data, datasize);
    +
    1192  }
    +
    1193 
    +
    1194 #ifdef LLRB234
    +
    1195  // split 4-nodes on the way down (2-3-4 exclusive)
    +
    1196  if (is_red(obj->left) && is_red(obj->right)) {
    +
    1197  flip_color(obj);
    +
    1198  }
    +
    1199 #endif
    +
    1200 
    +
    1201  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    1202  if (cmp == 0) { // existing key found
    +
    1203  void *copydata = qmemdup(data, datasize);
    +
    1204  if (copydata != NULL) {
    +
    1205  free(obj->data);
    +
    1206  obj->data = copydata;
    +
    1207  obj->datasize = datasize;
    +
    1208  }
    +
    1209  } else if (cmp < 0) {
    +
    1210  obj->left = put_obj(tbl, obj->left, name, namesize, data, datasize);
    +
    1211  } else {
    +
    1212  obj->right = put_obj(tbl, obj->right, name, namesize, data, datasize);
    +
    1213  }
    +
    1214 
    +
    1215  // fix right-leaning reds on the way up
    +
    1216  if (is_red(obj->right) && !is_red(obj->left)) {
    +
    1217  obj = rotate_left(obj);
    +
    1218  }
    +
    1219 
    +
    1220  // fix two reds in a row on the way up
    +
    1221  if (is_red(obj->left) && is_red(obj->left->left)) {
    +
    1222  obj = rotate_right(obj);
    +
    1223  }
    +
    1224 
    +
    1225 #ifndef LLRB234
    +
    1226  // split 4-nodes on the way up (2-3 exclusive)
    +
    1227  if (is_red(obj->left) && is_red(obj->right)) {
    +
    1228  flip_color(obj);
    +
    1229  }
    +
    1230 #endif
    +
    1231 
    +
    1232  // return new root
    +
    1233  return obj;
    +
    1234 }
    +
    1235 
    +
    1236 static qtreetbl_obj_t *remove_obj(qtreetbl_t *tbl, qtreetbl_obj_t *obj,
    +
    1237  const void *name, size_t namesize) {
    +
    1238  if (obj == NULL) {
    +
    1239  errno = ENOENT;
    +
    1240  return NULL;
    +
    1241  }
    +
    1242 
    +
    1243  int cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    1244  if (cmp < 0) {
    +
    1245  // move red left
    +
    1246  if (obj->left != NULL
    +
    1247  && (!is_red(obj->left) && !is_red(obj->left->left))) {
    +
    1248  obj = move_red_left(obj);
    +
    1249  }
    +
    1250  // keep going down to the left
    +
    1251  obj->left = remove_obj(tbl, obj->left, name, namesize);
    +
    1252  } else { // right or equal
    +
    1253  bool recmp = false; // optimization to reduce duplicated comparisions
    +
    1254  if (is_red(obj->left)) {
    +
    1255  obj = rotate_right(obj);
    +
    1256  recmp = true;
    +
    1257  }
    +
    1258  // remove if equal at the bottom
    +
    1259  if (obj->right == NULL) {
    +
    1260  if (recmp) {
    +
    1261  cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    1262  recmp = false;
    +
    1263  }
    +
    1264  if (cmp == 0) {
    +
    1265  free(obj->name);
    +
    1266  free(obj->data);
    +
    1267  free(obj);
    +
    1268  tbl->num--;
    +
    1269  return NULL;
    +
    1270  }
    +
    1271  }
    +
    1272  // move red right
    +
    1273  if (obj->right != NULL
    +
    1274  && (!is_red(obj->right) && !is_red(obj->right->left))) {
    +
    1275  obj = move_red_right(obj);
    +
    1276  recmp = true;
    +
    1277  }
    +
    1278  // found in the middle
    +
    1279  if (recmp) {
    +
    1280  cmp = tbl->compare(name, namesize, obj->name, obj->namesize);
    +
    1281  }
    +
    1282  if (cmp == 0) {
    +
    1283  // copy min to this then remove min
    +
    1284  qtreetbl_obj_t *minobj = find_min(obj->right);
    +
    1285  assert(minobj != NULL);
    +
    1286  free(obj->name);
    +
    1287  free(obj->data);
    +
    1288  obj->name = qmemdup(minobj->name, minobj->namesize);
    +
    1289  obj->namesize = minobj->namesize;
    +
    1290  obj->data = qmemdup(minobj->data, minobj->datasize);
    +
    1291  obj->datasize = minobj->datasize;
    +
    1292  obj->right = remove_min(obj->right);
    +
    1293  tbl->num--;
    +
    1294  } else {
    +
    1295  // keep going down to the right
    +
    1296  obj->right = remove_obj(tbl, obj->right, name, namesize);
    +
    1297  }
    +
    1298  }
    +
    1299  // fix right-leaning red nodes on the way up
    +
    1300  return fix(obj);
    +
    1301 }
    +
    1302 
    +
    1303 static void free_objs(qtreetbl_obj_t *obj) {
    +
    1304  if (obj == NULL) {
    +
    1305  return;
    +
    1306  }
    +
    1307 
    +
    1308  free_objs(obj->left);
    +
    1309  free_objs(obj->right);
    +
    1310 
    +
    1311  free(obj->name);
    +
    1312  free(obj->data);
    +
    1313  free(obj);
    +
    1314 }
    +
    1315 
    +
    1316 static uint8_t reset_iterator(qtreetbl_t *tbl) {
    +
    1317  if (tbl->root != NULL) {
    +
    1318  tbl->root->next = NULL;
    +
    1319  }
    +
    1320  return (++tbl->tid);
    +
    1321 }
    +
    1322 
    +
    1323 static void print_branch(struct branch_obj_s *branch, FILE *out) {
    +
    1324  if (branch == NULL) {
    +
    1325  return;
    +
    1326  }
    +
    1327  print_branch(branch->p, out);
    +
    1328  fprintf(out, "%s", branch->s);
    +
    1329 }
    +
    1330 
    +
    1331 static void print_node(qtreetbl_obj_t *obj, FILE *out, struct branch_obj_s *prev,
    +
    1332  bool right) {
    +
    1333  if (obj == NULL) {
    +
    1334  return;
    +
    1335  }
    +
    1336 
    +
    1337  struct branch_obj_s branch;
    +
    1338  branch.p = prev;
    +
    1339 
    +
    1340  branch.s = (prev != NULL) ? (right) ? " " : "│ " : "";
    +
    1341  print_node(obj->right, out, &branch, true);
    +
    1342 
    +
    1343  print_branch(prev, out);
    +
    1344  if (prev != NULL) {
    +
    1345  fprintf(out, "%s%s", (right) ? "┌──" : "└──", (obj->red) ? "[" : "─");
    +
    1346  }
    +
    1347  if (obj->data == NULL && obj->namesize == sizeof(uint32_t)) {
    +
    1348  fprintf(out, "%u", *(uint32_t *)obj->name);
    +
    1349  } else {
    +
    1350  _q_textout(out, obj->name, obj->namesize, 15);
    +
    1351  }
    +
    1352  fprintf(out, "%s", (obj->red) ? "]\n" : "\n");
    +
    1353 
    +
    1354  branch.s = (prev != NULL) ? (right) ? "│ " : " " : "";
    +
    1355  print_node(obj->left, out, &branch, false);
    +
    1356 }
    +
    1357 
    +
    1358 #endif
    +
    void * qmemdup(const void *data, size_t size)
    Duplicate a copy of memory data.
    Definition: qstring.c:413
    +
    void * qtreetbl_find_min(qtreetbl_t *tbl, size_t *namesize)
    qtreetbl->find_min(): Find the name of the leftmost object.
    Definition: qtreetbl.c:634
    +
    void * qtreetbl_find_max(qtreetbl_t *tbl, size_t *namesize)
    qtreetbl->find_max(): Find the name of the rightmost object.
    Definition: qtreetbl.c:662
    +
    int node_check_root(qtreetbl_t *tbl)
    Verifies that root property of the red-black tree is satisfied.
    Definition: qtreetbl.c:890
    +
    void qtreetbl_free(qtreetbl_t *tbl)
    qtreetbl->free(): De-allocate the table.
    Definition: qtreetbl.c:827
    +
    bool qtreetbl_putstr(qtreetbl_t *tbl, const char *name, const char *str)
    qtreetbl->putstr(): Put a string into this table.
    Definition: qtreetbl.c:287
    +
    bool qtreetbl_remove(qtreetbl_t *tbl, const char *name)
    qtreetbl->remove(): Remove an object from this table.
    Definition: qtreetbl.c:474
    +
    size_t qtreetbl_size(qtreetbl_t *tbl)
    qtreetbl->size(): Returns the number of keys in the table.
    Definition: qtreetbl.c:775
    +
    bool qtreetbl_put(qtreetbl_t *tbl, const char *name, const void *data, size_t datasize)
    qtreetbl->put(): Put an object into this table with string type key.
    Definition: qtreetbl.c:269
    +
    char * qtreetbl_getstr(qtreetbl_t *tbl, const char *name, const bool newmem)
    qtreetbl->getstr(): Finds an object and returns it as string type.
    Definition: qtreetbl.c:417
    +
    qtreetbl_t * qtreetbl(int options)
    Initialize a tree table.
    Definition: qtreetbl.c:182
    +
    void qtreetbl_lock(qtreetbl_t *tbl)
    qtreetbl->lock(): Enter critical section.
    Definition: qtreetbl.c:805
    +
    bool qtreetbl_debug(qtreetbl_t *tbl, FILE *out)
    qtreetbl->debug(): Print the internal tree structure in text.
    Definition: qtreetbl.c:871
    +
    bool qtreetbl_putobj(qtreetbl_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
    qtreetbl->putobj(): Put an object data into this table with an object key.
    Definition: qtreetbl.c:336
    +
    void * qtreetbl_getobj(qtreetbl_t *tbl, const void *name, size_t namesize, size_t *datasize, bool newmem)
    qtreetbl->getobj(): Get an object from this table with an object name.
    Definition: qtreetbl.c:443
    +
    void qtreetbl_clear(qtreetbl_t *tbl)
    qtreetbl->clear(): Clears the table so that it contains no keys.
    Definition: qtreetbl.c:784
    +
    void qtreetbl_unlock(qtreetbl_t *tbl)
    qtreetbl->unlock(): Leave critical section.
    Definition: qtreetbl.c:818
    +
    int node_check_llrb(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
    Verifies that LLRB property of the left-leaning red-black tree is satisfied.
    Definition: qtreetbl.c:971
    +
    qtreetbl_obj_t qtreetbl_find_nearest(qtreetbl_t *tbl, const void *name, size_t namesize, bool newmem)
    qtreetbl->find_nearest(): Find an object with matching key or nearest.
    Definition: qtreetbl.c:711
    +
    int node_check_black(qtreetbl_t *tbl, qtreetbl_obj_t *obj, int *path_len)
    Verifies that black property of the red-black tree is satisfied.
    Definition: qtreetbl.c:940
    +
    void * qtreetbl_get(qtreetbl_t *tbl, const char *name, size_t *datasize, bool newmem)
    qtreetbl->get(): Get an object from this table.
    Definition: qtreetbl.c:392
    +
    int qtreetbl_check(qtreetbl_t *tbl)
    Verifies that the invariants of the red-black tree are satisfied.
    Definition: qtreetbl.c:1001
    +
    bool qtreetbl_putstrf(qtreetbl_t *tbl, const char *name, const char *format,...)
    qtreetbl->putstrf(): Put a formatted string into this table.
    Definition: qtreetbl.c:305
    +
    int node_check_red(qtreetbl_t *tbl, qtreetbl_obj_t *obj)
    Verifies that red property of the red-black tree is satisfied.
    Definition: qtreetbl.c:909
    +
    bool qtreetbl_removeobj(qtreetbl_t *tbl, const void *name, size_t namesize)
    qtreetbl->remove(): Remove an object from this table with an object name.
    Definition: qtreetbl.c:491
    +
    bool qtreetbl_getnext(qtreetbl_t *tbl, qtreetbl_obj_t *obj, const bool newmem)
    qtreetbl->getnext(): Get next element.
    Definition: qtreetbl.c:580
    +
    void qtreetbl_set_compare(qtreetbl_t *tbl, int(*cmp)(const void *name1, size_t namesize1, const void *name2, size_t namesize2))
    qtreetbl->set_compare(): Set user comparator.
    Definition: qtreetbl.c:250
    diff --git a/doc/html/qvector_8c.html b/doc/html/qvector_8c.html index 3f25fbb1..b02cd7ff 100644 --- a/doc/html/qvector_8c.html +++ b/doc/html/qvector_8c.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qvector.c File Reference @@ -20,8 +20,8 @@
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,7 +52,7 @@
    @@ -61,7 +60,8 @@
    -
    qvector.c File Reference
    +
    +
    qvector.c File Reference
    @@ -70,88 +70,88 @@

    Go to the source code of this file.

    - - - - + - - - + + + - + - + - - - - - - - - - + + + + + + + + + - + - + - + - - - - - - - - - + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - +

    +

    Functions

    +
    static void * get_at (qvector_t *vector, int index, bool newmem)
     
    +
     
    static bool remove_at (qvector_t *vector, int index)
     
    qvector_t * qvector (size_t max, size_t objsize, int options)
     Create new qvector_t container.
     
    qvector_t * qvector (size_t max, size_t objsize, int options)
     Create new qvector_t container. More...
     
    bool qvector_addfirst (qvector_t *vector, const void *data)
     qvector->addfirst(): Insert a element at the beginning of this vector.
     qvector->addfirst(): Insert a element at the beginning of this vector. More...
     
    bool qvector_addlast (qvector_t *vector, const void *data)
     qvector->addlast(): Insert a element at the end of this vector.
     qvector->addlast(): Insert a element at the end of this vector. More...
     
    void * qvector_getfirst (qvector_t *vector, bool newmem)
     qvector->addat(): Inserts a element at the specified position in this vector.
     
    void * qvector_getlast (qvector_t *vector, bool newmem)
     qvector->getlast(): Returns the last element in this vector.
     
    void * qvector_getat (qvector_t *vector, int index, bool newmem)
     qvector->getat(): Returns the element at the specified position in this vector.
     
    void * qvector_getfirst (qvector_t *vector, bool newmem)
     qvector->addat(): Inserts a element at the specified position in this vector. More...
     
    void * qvector_getlast (qvector_t *vector, bool newmem)
     qvector->getlast(): Returns the last element in this vector. More...
     
    void * qvector_getat (qvector_t *vector, int index, bool newmem)
     qvector->getat(): Returns the element at the specified position in this vector. More...
     
    bool qvector_setfirst (qvector_t *vector, const void *data)
     qvector->setfirst(): Set the first element with a new value in this vector.
     qvector->setfirst(): Set the first element with a new value in this vector. More...
     
    bool qvector_setlast (qvector_t *vector, const void *data)
     qvector->setlast(): Set the last element with a new value in this vector.
     qvector->setlast(): Set the last element with a new value in this vector. More...
     
    bool qvector_setat (qvector_t *vector, int index, const void *data)
     qvector->setat(): Set new value to the specified position in this vector.
     qvector->setat(): Set new value to the specified position in this vector. More...
     
    void * qvector_popfirst (qvector_t *vector)
     qvector->popfirst(): Returns and remove the first element in this vector.
     
    void * qvector_poplast (qvector_t *vector)
     qvector->poplast(): Returns the last element of this vector.
     
    void * qvector_popat (qvector_t *vector, int index)
     qvector->popat(): Returns and remove the element at specified position in this vector.
     
    void * qvector_popfirst (qvector_t *vector)
     qvector->popfirst(): Returns and remove the first element in this vector. More...
     
    void * qvector_poplast (qvector_t *vector)
     qvector->poplast(): Returns the last element of this vector. More...
     
    void * qvector_popat (qvector_t *vector, int index)
     qvector->popat(): Returns and remove the element at specified position in this vector. More...
     
    bool qvector_removefirst (qvector_t *vector)
     qvector->removefirst(): Removes the first element in this vector.
     qvector->removefirst(): Removes the first element in this vector. More...
     
    bool qvector_removelast (qvector_t *vector)
     qvector->removelast(): Removes the last element in this vector.
     qvector->removelast(): Removes the last element in this vector. More...
     
    bool qvector_removeat (qvector_t *vector, int index)
     qvector->removeat(): Removes the element at the specified position in this vector.
     qvector->removeat(): Removes the element at the specified position in this vector. More...
     
    size_t qvector_size (qvector_t *vector)
     qvector->size(): Get the number of elements in this vector.
     qvector->size(): Get the number of elements in this vector. More...
     
    void qvector_lock (qvector_t *vector)
     qvector->lock(): Enters critical section.
     qvector->lock(): Enters critical section. More...
     
    void qvector_unlock (qvector_t *vector)
     qvector->unlock(): Leaves critical section.
     qvector->unlock(): Leaves critical section. More...
     
    void qvector_clear (qvector_t *vector)
     qvector->clear(): Remove all the elemnts in this vector.
     qvector->clear(): Remove all the elemnts in this vector. More...
     
    void qvector_free (qvector_t *vector)
     qvector->free(): Free this vector.
     qvector->free(): Free this vector. More...
     
    bool qvector_debug (qvector_t *vector, FILE *out)
     qvector->debug(): Prints out stored elements for debugging purpose.
     qvector->debug(): Prints out stored elements for debugging purpose. More...
     
    bool qvector_resize (qvector_t *vector, size_t newmax)
     qvector->resize(): Changes the allocated memory space size.
     qvector->resize(): Changes the allocated memory space size. More...
     
    void * qvector_toarray (qvector_t *vector, size_t *size)
     qvector->toarray(): Returns an array contains all the elements in this vector.
     
    void * qvector_toarray (qvector_t *vector, size_t *size)
     qvector->toarray(): Returns an array contains all the elements in this vector. More...
     
    void qvector_reverse (qvector_t *vector)
     qvector->reverse(): Reverse the order of element in this vector.
     qvector->reverse(): Reverse the order of element in this vector. More...
     
    bool qvector_getnext (qvector_t *vector, qvector_obj_t *obj, bool newmem)
     qvector->getnext(): Get next element in this vector.
     qvector->getnext(): Get next element in this vector. More...
     

    Detailed Description

    @@ -167,7 +167,7 @@
    @code
    //create a vector
    -
    qvector_t *vector = qvector(QVECTOR_THREADSAFE, 3, sizeof(int));
    +
    qvector_t *vector = qvector(QVECTOR_THREADSAFE, 3, sizeof(int));
    //insert elements
    vector->addlast(vector, 100);
    @@ -193,18 +193,18 @@
    //free vector object
    vector->free(vector);
    -
    qvector_t * qvector(size_t max, size_t objsize, int options)
    Create new qvector_t container.
    Definition qvector.c:117
    +
    qvector_t * qvector(size_t max, size_t objsize, int options)
    Create new qvector_t container.
    Definition: qvector.c:117

    Definition in file qvector.c.

    Function Documentation

    - -

    ◆ qvector()

    + +

    ◆ qvector()

    - + @@ -249,7 +249,7 @@

    qvector_t *vector = qvector(10, sizeof(int), 0);
    +
    qvector_t *vector = qvector(10, sizeof(int), 0);
    Note
    Available options:

    qvector_t * qvector qvector_t* qvector ( size_t  max,
    - + @@ -429,7 +429,7 @@

    @encode
    @code
    -
    +
    qvector_t *vector = qvector();
    vector->addat(vector, 0, &data); //same as addfirst().
    vector->addat(vector, 0, &data); //same as addlast().
    @@ -493,36 +493,38 @@

    }
    /**
    -
    qvector->getfirst(): Returns the first element in this vector.
    -
    -
    @param vector qvector_t container pointer.
    -
    @param newmem whether or not to allocate memory for the element.
    -
    -
    @return a pointer of element, otherwise returns NULL.
    -
    @retval errno will be set in error condition.
    -
    - ENOENT : Vector is empty.
    -
    - ENOMEM : Memory allocation failure.
    -
    -
    @code
    -
    size_t size;
    -
    void *data = vector->getfirst(vector, true);
    -
    if (data != NULL) {
    -
    (...omit...)
    -
    free(data);
    -
    }
    +
    * qvector->getfirst(): Returns the first element in this vector.
    +
    *
    +
    * @param vector qvector_t container pointer.
    +
    * @param newmem whether or not to allocate memory for the element.
    +
    *
    +
    * @return a pointer of element, otherwise returns NULL.
    +
    * @retval errno will be set in error condition.
    +
    * - ENOENT : Vector is empty.
    +
    * - ENOMEM : Memory allocation failure.
    +
    *
    +
    * @code
    +
    * size_t size;
    +
    * void *data = vector->getfirst(vector, true);
    +
    * if (data != NULL) {
    +
    * (...omit...)
    +
    * free(data);
    +
    * }
    +
    *
    +
    *

    Definition at line 371 of file qvector.c.

    - -

    ◆ qvector_getlast()

    + +

    ◆ qvector_getlast()

    void * qvector_getfirst void* qvector_getfirst ( qvector_t *  vector,
    - + @@ -570,14 +572,14 @@

    -

    ◆ qvector_getat()

    + +

    ◆ qvector_getat()

    void * qvector_getlast void* qvector_getlast ( qvector_t *  vector,
    - + @@ -632,8 +634,8 @@

    -

    ◆ qvector_setfirst()

    + +

    ◆ qvector_setfirst()

    @@ -678,7 +680,7 @@

    struct my_obj obj;
    //set values to obj;
    -
    +
    qvector_t *vector = qvector();
    vector->addlast();
    vector->setfirst(vector, &obj);

    @@ -686,8 +688,8 @@

    -

    ◆ qvector_setlast()

    + +

    ◆ qvector_setlast()

    @@ -732,7 +734,7 @@

    struct my_obj obj;
    //set values to obj;
    -
    +
    qvector_t *vector = qvector();
    vector->addlast();
    vector->setlast(vector, &obj);

    @@ -740,8 +742,8 @@

    -

    ◆ qvector_setat()

    + +

    ◆ qvector_setat()

    @@ -793,7 +795,7 @@

    struct my_obj obj;
    //set values to obj;
    -
    +
    qvector_t *vector = qvector();
    vector->addlast();
    vector->setat(vector, 0, &obj);

    @@ -801,14 +803,14 @@

    -

    ◆ qvector_popfirst()

    + +

    ◆ qvector_popfirst()

    void * qvector_getat void* qvector_getat ( qvector_t *  vector,
    - + @@ -840,14 +842,14 @@

    -

    ◆ qvector_poplast()

    + +

    ◆ qvector_poplast()

    void * qvector_popfirst void* qvector_popfirst ( qvector_t *  vector)
    - + @@ -879,14 +881,14 @@

    -

    ◆ qvector_popat()

    + +

    ◆ qvector_popat()

    void * qvector_poplast void* qvector_poplast ( qvector_t *  vector)
    - + @@ -935,8 +937,8 @@

    -

    ◆ qvector_removefirst()

    + +

    ◆ qvector_removefirst()

    @@ -974,8 +976,8 @@

    -

    ◆ qvector_removelast()

    + +

    ◆ qvector_removelast()

    @@ -1013,8 +1015,8 @@

    -

    ◆ qvector_removeat()

    + +

    ◆ qvector_removeat()

    @@ -1063,8 +1065,8 @@

    -

    ◆ qvector_size()

    + +

    ◆ qvector_size()

    @@ -1092,8 +1094,8 @@

    -

    ◆ qvector_lock()

    + +

    ◆ qvector_lock()

    @@ -1121,8 +1123,8 @@

    -

    ◆ qvector_unlock()

    + +

    ◆ qvector_unlock()

    @@ -1149,8 +1151,8 @@

    -

    ◆ qvector_clear()

    + +

    ◆ qvector_clear()

    @@ -1177,8 +1179,8 @@

    -

    ◆ qvector_free()

    + +

    ◆ qvector_free()

    @@ -1205,8 +1207,8 @@

    -

    ◆ qvector_debug()

    + +

    ◆ qvector_debug()

    @@ -1254,8 +1256,8 @@

    -

    ◆ qvector_resize()

    + +

    ◆ qvector_resize()

    @@ -1301,7 +1303,7 @@

    struct my_obj obj;

    //create a vector which allocates 4 * sizeof(obj) memory
    -
    qvector_t *vector = qvector(0, 4, sizeof(struct my_obj));
    +
    qvector_t *vector = qvector(0, 4, sizeof(struct my_obj));
    //expand the memory space of vector to 8 * sizeof(obj)
    vector->resize(vector, 8);
    @@ -1309,14 +1311,14 @@

    -

    ◆ qvector_toarray()

    + +

    ◆ qvector_toarray()

    void * qvector_popat void* qvector_popat ( qvector_t *  vector,
    - + @@ -1359,8 +1361,8 @@

    -

    ◆ qvector_reverse()

    + +

    ◆ qvector_reverse()

    @@ -1396,8 +1398,8 @@

    -

    ◆ qvector_getnext()

    + +

    ◆ qvector_getnext()

    @@ -1449,7 +1451,7 @@

    Note
    obj should be initialized with 0 by using memset() by the first call. If newmem flag is true, user should de-allocate obj.data resources.
    -
    +
    qvector_t *vector = qvector();
    (...add data into vector...)
    qvector_obj_t obj;
    @@ -1470,7 +1472,7 @@

    diff --git a/doc/html/qvector_8c.js b/doc/html/qvector_8c.js index 9a39b134..675dad9d 100644 --- a/doc/html/qvector_8c.js +++ b/doc/html/qvector_8c.js @@ -1,17 +1,19 @@ var qvector_8c = [ - [ "qvector", "qvector_8c.html#a6ed9e631160fd8f497a384dcb82d5794", null ], + [ "get_at", "qvector_8c.html#a51748a7e6721639ff37999b531c6e425", null ], + [ "remove_at", "qvector_8c.html#a4cb6c698932511561dba1574d0a18460", null ], + [ "qvector", "qvector_8c.html#ac8a262e93b18c9dc800f002389a9d645", null ], [ "qvector_addfirst", "qvector_8c.html#a0bf76991577f397aafecb52599709103", null ], [ "qvector_addlast", "qvector_8c.html#af61312ebeacce01f8d2b1777802d3f98", null ], - [ "qvector_getfirst", "qvector_8c.html#a377aea2cd35c7d879a35fb3c9404e85c", null ], - [ "qvector_getlast", "qvector_8c.html#a516f324b062fb12c15cc444b8f72798f", null ], - [ "qvector_getat", "qvector_8c.html#a131073e557942bf36271cb01a3ccaf43", null ], + [ "qvector_getfirst", "qvector_8c.html#a96c7398c8e03e820b7f5d016639bc6f3", null ], + [ "qvector_getlast", "qvector_8c.html#ad93462633f13508a3c1345525e7776e2", null ], + [ "qvector_getat", "qvector_8c.html#a73f4f6085f1fa68e03451790e292436f", null ], [ "qvector_setfirst", "qvector_8c.html#a9971c9a1bf24e25373b93fe2b8529d35", null ], [ "qvector_setlast", "qvector_8c.html#a843cf9ea1d613a5eb28e7c7ac957b1cf", null ], [ "qvector_setat", "qvector_8c.html#ae3290364eec4333b91c2b7314e6fee5a", null ], - [ "qvector_popfirst", "qvector_8c.html#a468e3d776f71310c1de156344ad67c12", null ], - [ "qvector_poplast", "qvector_8c.html#a160e7f61e805c0b89e50ba6c2d74ba13", null ], - [ "qvector_popat", "qvector_8c.html#add233a6a618bcb97d7216f706432445a", null ], + [ "qvector_popfirst", "qvector_8c.html#a5f7fac8917d39c653aaf57b0b5ef913b", null ], + [ "qvector_poplast", "qvector_8c.html#ae501069d92ff2c9d9c852eb702346e69", null ], + [ "qvector_popat", "qvector_8c.html#a7c361ad0a5caf3d3a4732cf087b178ab", null ], [ "qvector_removefirst", "qvector_8c.html#a0da63e7c76f7cf42db2e5e027bd51a02", null ], [ "qvector_removelast", "qvector_8c.html#aa2643b2f497925b48e746ef58fc50729", null ], [ "qvector_removeat", "qvector_8c.html#adc720c91738ed960fac9c0675d5aafbe", null ], @@ -22,7 +24,7 @@ var qvector_8c = [ "qvector_free", "qvector_8c.html#a69479bf55198bdd1c29e906d1b069480", null ], [ "qvector_debug", "qvector_8c.html#a217d5fe3cb59560703d84e47097dc989", null ], [ "qvector_resize", "qvector_8c.html#a304a57b5c428b4c517f1d0a22c84529b", null ], - [ "qvector_toarray", "qvector_8c.html#a7968834024b83c5f7d4e76935d5d9a0c", null ], + [ "qvector_toarray", "qvector_8c.html#ae367e5e5fc33e2eb5dc9eb8791efcb15", null ], [ "qvector_reverse", "qvector_8c.html#a11d7f8b4c7ac35bbfb60b168ae2a5046", null ], [ "qvector_getnext", "qvector_8c.html#a888474c5ca93c96452782cddb03ada82", null ] ]; \ No newline at end of file diff --git a/doc/html/qvector_8c_source.html b/doc/html/qvector_8c_source.html index 5538640d..c3feb136 100644 --- a/doc/html/qvector_8c_source.html +++ b/doc/html/qvector_8c_source.html @@ -1,9 +1,9 @@ - + - - + + qLibc: containers/qvector.c Source File @@ -20,8 +20,8 @@

    void * qvector_toarray void* qvector_toarray ( qvector_t *  vector,
    - - + @@ -30,16 +30,15 @@
    +
    qLibc
    - + +/* @license-end */
    @@ -53,1019 +52,1020 @@
    -
    qvector.c
    +
    +
    qvector.c
    -Go to the documentation of this file.
    1/******************************************************************************
    -
    2 * qLibc
    -
    3 *
    -
    4 * Copyright (c) 2010-2015 Seungyoung Kim.
    -
    5 * All rights reserved.
    -
    6 *
    -
    7 * Redistribution and use in source and binary forms, with or without
    -
    8 * modification, are permitted provided that the following conditions are met:
    -
    9 *
    -
    10 * 1. Redistributions of source code must retain the above copyright notice,
    -
    11 * this list of conditions and the following disclaimer.
    -
    12 * 2. Redistributions in binary form must reproduce the above copyright notice,
    -
    13 * this list of conditions and the following disclaimer in the documentation
    -
    14 * and/or other materials provided with the distribution.
    -
    15 *
    -
    16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    -
    17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -
    18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -
    19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    -
    20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    -
    21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    -
    22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    -
    23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    -
    24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -
    25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    -
    26 * POSSIBILITY OF SUCH DAMAGE.
    -
    27 *****************************************************************************/
    -
    28/* This code is written and updated by following people and released under
    -
    29 * the same license as above qLibc license.
    -
    30 * Copyright (c) 2015 Zhenjiang Xie - https://github.com/Charles0429
    -
    31 *****************************************************************************/
    -
    32
    -
    33/**
    -
    34 * @file qvector.c Vector container implementation.
    -
    35 *
    -
    36 * qvector container is a vector container implementation
    -
    37 * qvector provides uniformly named methods to add, get, pop and remove an
    -
    38 * element at the beginning and end of the vector.
    -
    39 *
    -
    40 * @code
    -
    41 * [Conceptional Data Structure Diagram]
    -
    42 *
    -
    43 * DATA [ C ][ B ][ A ]
    -
    44 * (positive index) 0 1 2
    -
    45 * (negative index) -3 -2 -1
    -
    46 *
    -
    47 * @encode
    -
    48 *
    -
    49 * @code
    -
    50 * //create a vector
    -
    51 * qvector_t *vector = qvector(QVECTOR_THREADSAFE, 3, sizeof(int));
    -
    52 *
    -
    53 * //insert elements
    -
    54 * vector->addlast(vector, 100);
    -
    55 * vector->addlast(vector, 101);
    -
    56 * vector->addlast(vector, 102);
    -
    57 *
    -
    58 * //get
    -
    59 * void *e1 = vector->getfirst(vector, true); //malloced
    -
    60 * void *e3 = vector->getlast(vector, false); //not malloced
    -
    61 * (...omit...)
    -
    62 * free(e1);
    -
    63 *
    -
    64 * //pop (get and remove)
    -
    65 * void *e2 = vector->popat(vector, 1); //get malloced copy
    -
    66 * (...omit...)
    -
    67 * free(e2);
    -
    68 *
    -
    69 * //debug output
    -
    70 * vector->debug(vector, stdout, true);
    -
    71 *
    -
    72 * //remove all the elements
    -
    73 * vector->clear(vector);
    -
    74 *
    -
    75 * //free vector object
    -
    76 * vector->free(vector);
    -
    77 * @endcode
    -
    78 */
    -
    79
    -
    80#include <stdio.h>
    -
    81#include <string.h>
    -
    82#include <errno.h>
    -
    83#include <stdbool.h>
    -
    84#include "qinternal.h"
    -
    85#include "containers/qvector.h"
    -
    86
    -
    87#ifndef _DOXGEN_SKIP
    -
    88
    -
    89static void *get_at(qvector_t *vector, int index, bool newmem);
    -
    90static bool remove_at(qvector_t *vector, int index);
    -
    91
    -
    92#endif
    -
    93
    -
    94/**
    -
    95 * Create new qvector_t container
    -
    96 *
    -
    97 * @param max max number of elements
    -
    98 * @param objsize size of each element
    -
    99 * @param options combination of initialization options.
    -
    100 *
    -
    101 * @return a pointer of malloced qvector_t container, otherwise returns NULL
    -
    102 * @retval errno will be set in error condition.
    -
    103 * - ENOMEM : Memory allocation failure.
    -
    104 * - EINVAL : Invalid argument.
    -
    105 *
    -
    106 * @code
    -
    107 * qvector_t *vector = qvector(10, sizeof(int), 0);
    -
    108 * @endcode
    -
    109 *
    -
    110 * @note
    -
    111 * Available options:
    -
    112 * - QVECTOR_THREADSAFE - make it thread-safe.
    -
    113 * - QVECTOR_RESIZE_DOUBLE - double the size when vector is full
    -
    114 * - QVECTOR_RESIZE_LINEAR - add the size with initial num when vector is full
    -
    115 * - QVECTOR_RESIZE_EXACT - add up as much as needed
    -
    116 */
    -
    117qvector_t *qvector(size_t max, size_t objsize, int options) {
    -
    118 if (objsize == 0) {
    -
    119 errno = EINVAL;
    -
    120 return NULL;
    -
    121 }
    -
    122
    -
    123 qvector_t *vector = (qvector_t *)calloc(1, sizeof(qvector_t));
    -
    124 if (vector == NULL) {
    -
    125 errno = ENOMEM;
    -
    126 return NULL;
    -
    127 }
    -
    128
    -
    129 if (max == 0) {
    -
    130 vector->data = NULL;
    -
    131 vector->num = 0;
    -
    132 vector->max = 0;
    -
    133 vector->objsize = objsize;
    -
    134 } else {
    -
    135 void *data = malloc(max * objsize);
    -
    136 if (data == NULL) {
    -
    137 free(vector);
    -
    138 errno = ENOMEM;
    -
    139 return NULL;
    -
    140 }
    -
    141
    -
    142 vector->data = data;
    -
    143 vector->num = 0;
    -
    144 vector->objsize = objsize;
    -
    145 vector->max = max;
    -
    146 }
    -
    147
    -
    148 //handle options
    -
    149 if (options & QVECTOR_THREADSAFE) {
    -
    150 Q_MUTEX_NEW(vector->qmutex, true);
    -
    151 if (vector->qmutex == NULL) {
    -
    152 free(vector);
    -
    153 errno = ENOMEM;
    -
    154 return NULL;
    -
    155 }
    -
    156 }
    -
    157
    -
    158 vector->options = 0;
    -
    159 if (options & QVECTOR_RESIZE_DOUBLE) {
    -
    160 vector->options |= QVECTOR_RESIZE_DOUBLE;
    -
    161 } else if (options & QVECTOR_RESIZE_LINEAR) {
    -
    162 vector->options |= QVECTOR_RESIZE_LINEAR;
    -
    163 if (max == 0) {
    -
    164 vector->initnum = 1;
    -
    165 } else {
    -
    166 vector->initnum = max;
    -
    167 }
    -
    168 } else {
    -
    169 vector->options |= QVECTOR_RESIZE_EXACT;
    -
    170 }
    -
    171
    -
    172 //member methods
    -
    173 vector->addfirst = qvector_addfirst;
    -
    174 vector->addlast = qvector_addlast;
    -
    175 vector->addat = qvector_addat;
    -
    176
    -
    177 vector->getfirst = qvector_getfirst;
    -
    178 vector->getlast = qvector_getlast;
    -
    179 vector->getat = qvector_getat;
    -
    180
    -
    181 vector->setfirst = qvector_setfirst;
    -
    182 vector->setlast = qvector_setlast;
    -
    183 vector->setat = qvector_setat;
    -
    184
    -
    185 vector->popfirst = qvector_popfirst;
    -
    186 vector->poplast = qvector_poplast;
    -
    187 vector->popat = qvector_popat;
    -
    188
    -
    189 vector->removefirst = qvector_removefirst;
    -
    190 vector->removelast = qvector_removelast;
    -
    191 vector->removeat = qvector_removeat;
    -
    192
    -
    193 vector->size = qvector_size;
    -
    194 vector->resize = qvector_resize;
    -
    195
    -
    196 vector->toarray = qvector_toarray;
    -
    197
    -
    198 vector->lock = qvector_lock;
    -
    199 vector->unlock = qvector_unlock;
    -
    200
    -
    201 vector->clear = qvector_clear;
    -
    202 vector->debug = qvector_debug;
    -
    203 vector->free = qvector_free;
    -
    204
    -
    205 vector->reverse = qvector_reverse;
    -
    206 vector->getnext = qvector_getnext;
    -
    207
    -
    208 return vector;
    -
    209}
    -
    210
    -
    211/**
    -
    212 * qvector->addfirst(): Insert a element at the beginning of this vector.
    -
    213 *
    -
    214 * @param vector qvector_t container pointer.
    -
    215 * @param data a pointer which points data memory
    -
    216 *
    -
    217 * @return true if successful, otherwise returns false.
    -
    218 * @retval errno will be set in error condition.
    -
    219 *
    -
    220 * - EINVAL : Invalid argument.
    -
    221 * - ENOMEM : Memory allocation failure.
    -
    222 *
    -
    223 * @code
    -
    224 * //create a sample object.
    -
    225 * struct my_obj obj;
    -
    226 *
    -
    227 * //create a vector and add the sample object.
    -
    228 * qvector_t *vector = qvector(0, 1, sizeof(struct my_obj));
    -
    229 * vector->addfirst(vector, &obj);
    -
    230 *
    -
    231 * @endcode
    -
    232 */
    -
    233bool qvector_addfirst(qvector_t *vector, const void *data) {
    -
    234 return vector->addat(vector, 0, data);
    -
    235}
    -
    236
    -
    237/**
    -
    238 * qvector->addlast(): Insert a element at the end of this vector.
    -
    239 *
    -
    240 * @param vector qvector_t container pointer.
    -
    241 * @param data a pointer which points data memory
    -
    242 *
    -
    243 * @return true if successful, otherwise returns false.
    -
    244 * @retval errno will be set in error condition.
    -
    245 *
    -
    246 * - EINVAL : Invalid argument.
    -
    247 * - ENOMEM : Memory allocation failure.
    -
    248 *
    -
    249 * @code
    -
    250 * //create a sample object.
    -
    251 * struct my_obj obj;
    -
    252 *
    -
    253 * //create a vector and add the sample object.
    -
    254 * qvector_t *vector = qvector(0, 1, sizeof(struct my_obj));
    -
    255 * vector->addlast(vector, &obj);
    -
    256 *
    -
    257 * @endcode
    -
    258 */
    -
    259bool qvector_addlast(qvector_t *vector, const void *data) {
    -
    260 return vector->addat(vector, vector->num, data);
    -
    261}
    -
    262
    -
    263/**
    -
    264 * qvector->addat(): Inserts a element at the specified position in this
    -
    265 * vector.
    -
    266 *
    -
    267 * @param vector qvector_t container pointer
    -
    268 * @param index index at which the specified element is to be inserted
    -
    269 * @param data a pointer which points data memory
    -
    270 *
    -
    271 * @return true if successful, otherwise returns false.
    -
    272 * @retval errno will be set in errno condition.
    -
    273 *
    -
    274 * - ERANGE : Index out of range.
    -
    275 * - EINVAL : Invalid argument.
    -
    276 * - ENOMEM : Memory allocation failure.
    -
    277 *
    -
    278 * @code
    -
    279 * first last new
    -
    280 * Array [ A ][ B ][ C ]?==?[ ]
    -
    281 * (positive index) 0 1 2 3
    -
    282 * (negative index) -3 -2 -1
    -
    283 *
    -
    284 * @encode
    -
    285 *
    -
    286 * @code
    -
    287 * qvector_t *vector = qvector();
    -
    288 * vector->addat(vector, 0, &data); //same as addfirst().
    -
    289 * vector->addat(vector, 0, &data); //same as addlast().
    -
    290 *
    -
    291 * @encode
    -
    292 *
    -
    293 * @note
    -
    294 * Index starts from 0.
    -
    295 */
    -
    296bool qvector_addat(qvector_t *vector, int index, const void *data) {
    -
    297 //check arguments
    -
    298 if (data == NULL) {
    -
    299 errno = EINVAL;
    -
    300 return false;
    -
    301 }
    -
    302
    -
    303 //check index
    -
    304 if (index < 0) {
    -
    305 index += vector->num;
    -
    306 }
    -
    307 if (index > vector->num) {
    -
    308 errno = ERANGE;
    -
    309 return false;
    -
    310 }
    -
    311
    -
    312 vector->lock(vector);
    -
    313
    -
    314 //check whether the vector is full
    -
    315 if (vector->num >= vector->max) {
    -
    316 size_t newmax = vector->max + 1;
    -
    317 if (vector->options & QVECTOR_RESIZE_DOUBLE) {
    -
    318 newmax = (vector->max + 1) * 2;
    -
    319 } else if (vector->options & QVECTOR_RESIZE_LINEAR) {
    -
    320 newmax = vector->max + vector->initnum;
    -
    321 } else {
    -
    322 newmax = vector->max + 1;
    -
    323 }
    -
    324 bool result = vector->resize(vector, newmax);
    -
    325 if (result == false)
    -
    326 {
    -
    327 vector->unlock(vector);
    -
    328 errno = ENOMEM;
    -
    329 return false;
    -
    330 }
    -
    331 }
    -
    332
    -
    333 //shift data from index...(num - 1) to index + 1...num
    -
    334 int i;
    -
    335 for (i = vector->num; i > index; i--) {
    -
    336 void *dst = (unsigned char *)vector->data + vector->objsize * i;
    -
    337 void *src = (unsigned char *)vector->data + vector->objsize * (i - 1);
    -
    338
    -
    339 memcpy(dst, src, vector->objsize);
    -
    340 }
    -
    341
    -
    342 void *add = (unsigned char *)vector->data + index * vector->objsize;
    -
    343 memcpy(add, data, vector->objsize);
    -
    344 vector->num++;
    -
    345
    -
    346 vector->unlock(vector);
    -
    347 return true;
    -
    348}
    -
    349
    -
    350/**
    -
    351 * qvector->getfirst(): Returns the first element in this vector.
    -
    352 *
    -
    353 * @param vector qvector_t container pointer.
    -
    354 * @param newmem whether or not to allocate memory for the element.
    -
    355 *
    -
    356 * @return a pointer of element, otherwise returns NULL.
    -
    357 * @retval errno will be set in error condition.
    -
    358 * - ENOENT : Vector is empty.
    -
    359 * - ENOMEM : Memory allocation failure.
    -
    360 *
    -
    361 * @code
    -
    362 * size_t size;
    -
    363 * void *data = vector->getfirst(vector, true);
    -
    364 * if (data != NULL) {
    -
    365 * (...omit...)
    -
    366 * free(data);
    -
    367 * }
    -
    368 *
    -
    369 * @endcode
    -
    370 */
    -
    371void *qvector_getfirst(qvector_t *vector, bool newmem) {
    -
    372 return vector->getat(vector, 0, newmem);
    -
    373}
    -
    374
    -
    375/**
    -
    376 * qvector->getlast(): Returns the last element in this vector.
    -
    377 *
    -
    378 * @param vector qvector_t container pointer.
    -
    379 * @param newmem whether or not to allocate memory for the element.
    -
    380 *
    -
    381 * @return a pointer of element, otherwise returns NULL.
    -
    382 * @retval errno will be set in error condition.
    -
    383 * - ENOENT : Vector is empty.
    -
    384 * - ENOMEM : Memory alocation failure.
    -
    385 *
    -
    386 * @code
    -
    387 * void *data = vector->getlast(vector, true);
    -
    388 * if (data != NULL) {
    -
    389 * (...omit...)
    -
    390 * free(data);
    -
    391 * }
    -
    392 *
    -
    393 * @endcode
    -
    394 */
    -
    395void *qvector_getlast(qvector_t *vector, bool newmem) {
    -
    396 return vector->getat(vector, -1, newmem);
    -
    397}
    -
    398
    -
    399/**
    -
    400 * qvector->getat(): Returns the element at the specified position in this
    -
    401 * vector.
    -
    402 *
    -
    403 * @param vector qvector_t container pointer.
    -
    404 * @param index index at which the specified element is to access.
    -
    405 * @param newmem whether or not to allocate memory for the element.
    -
    406 *
    -
    407 * @return a pointer of element, otherwise returns NULL.
    -
    408 * @retval errno will be set in error condition.
    -
    409 * - ERANGE : Index out of range.
    -
    410 * - ENOMEM : Memory allocation failure.
    -
    411 *
    -
    412 * @code
    -
    413 * first last
    -
    414 * Array [ A ][ B ][ C ]
    -
    415 * (positive index) 0 1 2
    -
    416 * (negative index) -1 -2 -3
    -
    417 *
    -
    418 * @endcode
    -
    419 *
    -
    420 * @note
    -
    421 * Index starts from 0.
    -
    422 */
    -
    423void *qvector_getat(qvector_t *vector, int index, bool newmem) {
    -
    424 vector->lock(vector);
    -
    425 void *data = get_at(vector, index, newmem);
    -
    426 vector->unlock(vector);
    -
    427
    -
    428 return data;
    -
    429}
    -
    430
    -
    431/**
    -
    432 * qvector->setfirst(): Set the first element with a new value in this
    -
    433 * vector.
    -
    434 *
    -
    435 * @param vector qvector_t container pointer.
    -
    436 * @param data the pointer of new value.
    -
    437 *
    -
    438 * @returns true if successful, otherwise returns false.
    -
    439 * @retval errno will be set in error condition.
    -
    440 * - ENOENT : Vector is empty.
    -
    441 *
    -
    442 * @code
    -
    443 *
    -
    444 * struct my_obj obj;
    -
    445 * //set values to obj;
    -
    446 * qvector_t *vector = qvector();
    -
    447 * vector->addlast();
    -
    448 * vector->setfirst(vector, &obj);
    -
    449 *
    -
    450 * @endcode
    -
    451 */
    -
    452bool qvector_setfirst(qvector_t *vector, const void *data) {
    -
    453 return vector->setat(vector, 0, data);
    -
    454}
    -
    455
    -
    456/**
    -
    457 * qvector->setlast(): Set the last element with a new value in this
    -
    458 * vector.
    -
    459 *
    -
    460 * @param vector qvector_t container pointer.
    -
    461 * @param data the pointer of new value.
    -
    462 *
    -
    463 * @returns true if successful, otherwise returns false.
    -
    464 * @retval errno will be set in error condition.
    -
    465 * - ENOENT : Vector is empty.
    -
    466 *
    -
    467 * @code
    -
    468 *
    -
    469 * struct my_obj obj;
    -
    470 * //set values to obj;
    -
    471 * qvector_t *vector = qvector();
    -
    472 * vector->addlast();
    -
    473 * vector->setlast(vector, &obj);
    -
    474 *
    -
    475 * @endcode
    -
    476 */
    -
    477bool qvector_setlast(qvector_t *vector, const void *data) {
    -
    478 return vector->setat(vector, -1, data);
    -
    479}
    -
    480
    -
    481/**
    -
    482 * qvector->setat(): Set new value to the specified position in this
    -
    483 * vector.
    -
    484 *
    -
    485 * @param vector qvector_t container pointer
    -
    486 * @param index index at which the specifed element is to set
    -
    487 * @param data the pointer of new value to be set
    -
    488 *
    -
    489 * @return true if successful, otherwise return false.
    -
    490 * @retval errno will be set in error condition.
    -
    491 * - ERANGE : Index out of range.
    -
    492 *
    -
    493 * @code
    -
    494 *
    -
    495 * struct my_obj obj;
    -
    496 * //set values to obj;
    -
    497 * qvector_t *vector = qvector();
    -
    498 * vector->addlast();
    -
    499 * vector->setat(vector, 0, &obj);
    -
    500 *
    -
    501 * @endcode
    -
    502 */
    -
    503bool qvector_setat(qvector_t *vector, int index, const void *data) {
    -
    504 vector->lock(vector);
    -
    505 void *old_data = get_at(vector, index, false);
    -
    506 if (old_data == NULL) {
    -
    507 return false;
    -
    508 }
    -
    509 memcpy(old_data, data, vector->objsize);
    -
    510 vector->unlock(vector);
    -
    511
    -
    512 return true;
    -
    513}
    -
    514
    -
    515/**
    -
    516 * qvector->popfirst(): Returns and remove the first element in this vector.
    -
    517 *
    -
    518 * @param vector qvector_t container pointer.
    -
    519 *
    -
    520 * @return a pointer of malloced element, otherwise returns NULL.
    -
    521 * @retval errno will be set in error condition.
    -
    522 * - ENOENT : Vector is empty.
    -
    523 * - ENOMEM : Memory allocation failure.
    -
    524 */
    -
    525void *qvector_popfirst(qvector_t *vector) {
    -
    526 return vector->popat(vector, 0);
    -
    527}
    -
    528
    -
    529/**
    -
    530 * qvector->poplast(): Returns the last element of this vector.
    -
    531 *
    -
    532 * @param vector qvector_t container pointer.
    -
    533 *
    -
    534 * @return a pointer of malloced element, otherwise returns NULL.
    -
    535 * @retval errno will be set in error condition.
    -
    536 * - ENOENT : Vector is empty.
    -
    537 * - ENOMEM : Memeory allocation failure.
    -
    538 */
    -
    539void *qvector_poplast(qvector_t *vector) {
    -
    540 return vector->popat(vector, -1);
    -
    541}
    -
    542
    -
    543/**
    -
    544 * qvector->popat(): Returns and remove the element at specified
    -
    545 * position in this vector.
    -
    546 *
    -
    547 * @param vector qvector_t container pointer.
    -
    548 * @param index index at which the specified element is to be poped.
    -
    549 *
    -
    550 * @return a pointer of malloced element, otherwise returns NULL.
    -
    551 * @retval errno will be set in error condition.
    -
    552 * - ENOENT : Vector is empty.
    -
    553 * - ERANGE : Index out of range.
    -
    554 * - ENOMEM : Mmemory allocation failure.
    -
    555 *
    -
    556 * @code
    -
    557 * first last
    -
    558 * Array [ A ][ B ][ C ]
    -
    559 * (positive index) 1 2 3
    -
    560 * (negative index) -1 -2 -3
    -
    561 *
    -
    562 * @endcode
    -
    563 *
    -
    564 * @note
    -
    565 * Index starts from 0.
    -
    566 */
    -
    567void *qvector_popat(qvector_t *vector, int index) {
    -
    568 vector->lock(vector);
    -
    569 void *data = get_at(vector, index, true);
    -
    570 if (data == NULL) {
    -
    571 return NULL;
    -
    572 }
    -
    573
    -
    574 bool result = remove_at(vector, index);
    -
    575 if (result == false) {
    -
    576 free(data);
    -
    577 vector->unlock(vector);
    -
    578 return NULL;
    -
    579 }
    -
    580 vector->num--;
    -
    581
    -
    582 vector->unlock(vector);
    -
    583 return data;
    -
    584}
    -
    585
    -
    586/**
    -
    587 * qvector->removefirst(): Removes the first element in this vector.
    -
    588 *
    -
    589 * @param vector qvector_t container pointer.
    -
    590 *
    -
    591 * @return true, otherwise returns false.
    -
    592 * @retval errno will be set in error condition.
    -
    593 * - ENOENT : Vector is empty.
    -
    594 * - ERANGE : Index out of range.
    -
    595 */
    -
    596bool qvector_removefirst(qvector_t *vector) {
    -
    597 return vector->removeat(vector, 0);
    -
    598}
    -
    599
    -
    600/**
    -
    601 * qvector->removelast(): Removes the last element in this vector.
    -
    602 *
    -
    603 * @param vector qvector_t container pointer.
    -
    604 *
    -
    605 * @return true, otherwise returns false.
    -
    606 * @retval errno will be set in error condition.
    -
    607 * - ENOENT : Vector is empty.
    -
    608 * - ERANGE : Index out of range.
    -
    609 */
    -
    610bool qvector_removelast(qvector_t *vector) {
    -
    611 return vector->removeat(vector, -1);
    -
    612}
    -
    613
    -
    614/**
    -
    615 * qvector->removeat(): Removes the element at the specified position in this vector.
    -
    616 *
    -
    617 * @param vector qvector_t container pointer.
    -
    618 * @param index index at which the specified element is to be removed.
    -
    619 *
    -
    620 * @return true, otherwise returns false.
    -
    621 * @retval errno will be set in error condition.
    -
    622 * - ENOENT : Vector is empty.
    -
    623 * - ERANGE : Index out of range.
    -
    624 */
    -
    625bool qvector_removeat(qvector_t *vector, int index) {
    -
    626 vector->lock(vector);
    -
    627 bool result = remove_at(vector, index);
    -
    628 if (result) {
    -
    629 vector->num--;
    -
    630 }
    -
    631
    -
    632 vector->unlock(vector);
    -
    633
    -
    634 return result;
    -
    635}
    -
    636
    -
    637/**
    -
    638 * qvector->size(): Get the number of elements in this vector.
    -
    639 *
    -
    640 * @param vector qvector_t container pointer.
    -
    641 *
    -
    642 * @return the number of elements in this vector.
    -
    643 */
    -
    644size_t qvector_size(qvector_t *vector) {
    -
    645 return vector->num;
    -
    646}
    -
    647
    -
    648/**
    -
    649 * qvector->lock(): Enters critical section.
    -
    650 *
    -
    651 * @param vector qvector_t container pointer.
    -
    652 *
    -
    653 * @note
    -
    654 * From user side, normally locking operation is only needed when traverse all
    -
    655 * elements using qvector->getnext().
    -
    656 */
    -
    657void qvector_lock(qvector_t *vector) {
    -
    658 Q_MUTEX_ENTER(vector->qmutex);
    -
    659}
    -
    660
    -
    661/**
    -
    662 * qvector->unlock(): Leaves critical section.
    -
    663 *
    -
    664 * @param vector qvector_t container pointer.
    -
    665 */
    -
    666void qvector_unlock(qvector_t *vector) {
    -
    667 Q_MUTEX_LEAVE(vector->qmutex);
    -
    668}
    -
    669
    -
    670/**
    -
    671 * qvector->clear(): Remove all the elemnts in this vector.
    -
    672 *
    -
    673 * @param vector qvector_t container pointer.
    -
    674 */
    -
    675void qvector_clear(qvector_t *vector) {
    -
    676 vector->lock(vector);
    -
    677 vector->num = 0;
    -
    678 vector->unlock(vector);
    -
    679}
    -
    680
    -
    681/**
    -
    682 * qvector->free(): Free this vector.
    -
    683 *
    -
    684 * @param vector qvector_t container pointer.
    -
    685 */
    -
    686void qvector_free(qvector_t *vector) {
    -
    687 vector->clear(vector);
    -
    688 Q_MUTEX_DESTROY(vector->qmutex);
    -
    689
    -
    690 if (vector->data != NULL) {
    -
    691 free(vector->data);
    -
    692 }
    -
    693
    -
    694 free(vector);
    -
    695}
    -
    696
    -
    697/**
    -
    698 * qvector->debug(): Prints out stored elements for debugging purpose.
    -
    699 *
    -
    700 * @param vector qvector_t container pointer.
    -
    701 * @param out output stream FILE descriptor such like stdout, stderr.
    -
    702 *
    -
    703 * @return true if successful, otherwise returns false.
    -
    704 * @retval errno will be set in error condition.
    -
    705 * - EIO : Invalid output stream.
    -
    706 */
    -
    707bool qvector_debug(qvector_t *vector, FILE *out) {
    -
    708 if (out == NULL) {
    -
    709 errno = EIO;
    -
    710 return false;
    -
    711 }
    -
    712
    -
    713 vector->lock(vector);
    -
    714 int i;
    -
    715 for (i = 0; i < vector->num; i++) {
    -
    716 void *data = (unsigned char *)vector->data + i * vector->objsize;
    -
    717 fprintf(out, "%d=", i);
    -
    718 _q_textout(out, data, vector->objsize, MAX_HUMANOUT);
    -
    719 fprintf(out, " (%zu)\n", vector->objsize);
    -
    720 }
    -
    721 vector->unlock(vector);
    -
    722
    -
    723 return true;
    -
    724}
    -
    725
    -
    726/**
    -
    727 * qvector->resize(): Changes the allocated memory space size.
    -
    728 *
    -
    729 * @param vector qvector_t container pointer.
    -
    730 * @param newsize the new max number of elements.
    -
    731 *
    -
    732 * @retval errno will be set in error condition.
    -
    733 * - ENOMEM : Memory allocation failure.
    -
    734 *
    -
    735 * @code
    -
    736 * //create a sample object.
    -
    737 * struct my_obj obj;
    -
    738 *
    -
    739 * //create a vector which allocates 4 * sizeof(obj) memory
    -
    740 * qvector_t *vector = qvector(0, 4, sizeof(struct my_obj));
    -
    741 * //expand the memory space of vector to 8 * sizeof(obj)
    -
    742 * vector->resize(vector, 8);
    -
    743 *
    -
    744 * @endcode
    -
    745 */
    -
    746bool qvector_resize(qvector_t *vector, size_t newmax) {
    -
    747 vector->lock(vector);
    -
    748
    -
    749 if (newmax == 0) {
    -
    750 free(vector->data);
    -
    751 vector->data = NULL;
    -
    752 vector->max = 0;
    -
    753 vector->num = 0;
    -
    754 vector->objsize = 0;
    -
    755
    -
    756 vector->unlock(vector);
    -
    757 return true;
    -
    758 }
    -
    759
    -
    760 void *newdata = realloc(vector->data, newmax * vector->objsize);
    -
    761 if (newdata == NULL) {
    -
    762 errno = ENOMEM;
    -
    763 vector->unlock(vector);
    -
    764 return false;
    -
    765 }
    -
    766
    -
    767 vector->data = newdata;
    -
    768 vector->max = newmax;
    -
    769 if (vector->num > newmax) {
    -
    770 vector->num = newmax;
    -
    771 }
    -
    772
    -
    773 vector->unlock(vector);
    -
    774 return true;
    -
    775}
    -
    776
    -
    777/**
    -
    778 * qvector->toarray(): Returns an array contains all the elements in this vector.
    -
    779 * @param vector qvector_t container pointer.
    -
    780 * @param size if size is not NULL, the number of elements will be stored.
    -
    781 *
    -
    782 * @return a malloced pointer, otherwise return NULL.
    -
    783 * @retval errno wil be set in error condition.
    -
    784 * - ENOENT : Vector is empty.
    -
    785 * - ENOMEM : Memory allocation failure.
    -
    786 */
    -
    787void *qvector_toarray(qvector_t *vector, size_t *size) {
    -
    788 if (vector->num <= 0) {
    -
    789 if (size != NULL) {
    -
    790 *size = 0;
    -
    791 }
    -
    792 errno = ENOENT;
    -
    793 return NULL;
    -
    794 }
    -
    795
    -
    796 vector->lock(vector);
    -
    797
    -
    798 void *array = malloc(vector->num * vector->objsize);
    -
    799 if (array == NULL) {
    -
    800 vector->unlock(vector);
    -
    801 errno = ENOMEM;
    -
    802 return NULL;
    -
    803 }
    -
    804
    -
    805 memcpy(array, vector->data, vector->num * vector->objsize);
    -
    806
    -
    807 if (size != NULL) {
    -
    808 *size = vector->num;
    -
    809 }
    -
    810
    -
    811 vector->unlock(vector);
    -
    812 return array;
    -
    813}
    -
    814
    -
    815/**
    -
    816 * qvector->reverse(): Reverse the order of element in this vector.
    -
    817 *
    -
    818 * @param vector qvector_t container pointer.
    -
    819 *
    -
    820 * @retval will be set in error condition.
    -
    821 * - ENOMEM : Memory allocations failure.
    -
    822 */
    -
    823void qvector_reverse(qvector_t *vector) {
    -
    824 vector->lock(vector);
    -
    825
    -
    826 if (vector->num <= 1) {
    -
    827 vector->unlock(vector);
    -
    828 return;
    -
    829 }
    -
    830
    -
    831 int i;
    -
    832 int j;
    -
    833 void *tmp = malloc(vector->objsize);
    -
    834 if (tmp == NULL) {
    -
    835 errno = ENOMEM;
    -
    836 return;
    -
    837 }
    -
    838
    -
    839 for (i = 0, j = vector->num - 1; i < j; i++, j--) {
    -
    840 void *data1 = (unsigned char *)vector->data + i * vector->objsize;
    -
    841 void *data2 = (unsigned char *)vector->data + j * vector->objsize;
    -
    842
    -
    843 memcpy(tmp, data1, vector->objsize);
    -
    844 memcpy(data1, data2, vector->objsize);
    -
    845 memcpy(data2, tmp, vector->objsize);
    -
    846 }
    -
    847 free(tmp);
    -
    848
    -
    849 vector->unlock(vector);
    -
    850}
    -
    851
    -
    852/**
    -
    853 * qvector->getnext(): Get next element in this vector.
    -
    854 *
    -
    855 * @param vector qvector_t container pointer.
    -
    856 * @param obj found data will be stored in this structure.
    -
    857 * @param newmem whether or not to allocate memory for element.
    -
    858 *
    -
    859 * @return true if found, otherwise return fasle.
    -
    860 * @retval errno will be set in error condition.
    -
    861 * - ENOENT : No next element.
    -
    862 * - ENOMEM : Memory allocation failure.
    -
    863 *
    -
    864 * @note
    -
    865 * obj should be initialized with 0 by using memset() by the first call.
    -
    866 * If newmem flag is true, user should de-allocate obj.data resources.
    -
    867 *
    -
    868 * @code
    -
    869 * qvector_t *vector = qvector();
    -
    870 * (...add data into vector...)
    -
    871 *
    -
    872 * qvector_obj_t obj;
    -
    873 * memset((void *)&obj, 0, sizeof(obj));
    -
    874 * vector->lock(vector);
    -
    875 * while(vector->getnext(vector, &obj, false) == true) {
    -
    876 * printf("DATA=%s\n", obj.data);
    -
    877 * }
    -
    878 * vector->unlock(vector);
    -
    879 * @endcode
    -
    880 */
    -
    881bool qvector_getnext(qvector_t *vector, qvector_obj_t *obj, bool newmem) {
    -
    882 if (obj == NULL) {
    -
    883 return false;
    -
    884 }
    -
    885 vector->lock(vector);
    -
    886
    -
    887 if (obj->index >= vector->num) {
    -
    888 errno = ENOENT;
    -
    889 obj->data = NULL;
    -
    890 vector->unlock(vector);
    -
    891 return false;
    -
    892 }
    -
    893
    -
    894 void *data = (unsigned char *)vector->data + (obj->index) * vector->objsize;
    -
    895 if (newmem) {
    -
    896 void *dump = malloc(vector->objsize);
    -
    897 if (dump == NULL ) {
    -
    898 errno = ENOMEM;
    -
    899 obj->data = NULL;
    -
    900 vector->unlock(vector);
    -
    901 return false;
    -
    902 }
    -
    903 memcpy(dump, data, vector->objsize);
    -
    904 obj->data = dump;
    -
    905 }
    -
    906 else
    -
    907 {
    -
    908 obj->data = data;
    -
    909 }
    -
    910
    -
    911 obj->index++;
    -
    912 vector->unlock(vector);
    -
    913 return true;
    -
    914}
    -
    915
    -
    916#ifndef _DOXYGEN_SKIP
    -
    917
    -
    918static void *get_at(qvector_t *vector, int index, bool newmem) {
    -
    919 if (index < 0) {
    -
    920 index += vector->num;
    -
    921 }
    -
    922 if (index >= vector->num) {
    -
    923 if (vector->num == 0) {
    -
    924 errno = ENOENT;
    -
    925 return NULL;
    -
    926 } else {
    -
    927 errno = ERANGE;
    -
    928 return NULL;
    -
    929 }
    -
    930 }
    -
    931
    -
    932 void *src_data = (unsigned char *)vector->data + index * vector->objsize;
    -
    933 if (newmem) {
    -
    934 void *dump_data = malloc(vector->objsize);
    -
    935 if (dump_data == NULL) {
    -
    936 errno = ENOMEM;
    -
    937 return NULL;
    -
    938 } else {
    -
    939 memcpy(dump_data, src_data, vector->objsize);
    -
    940 return dump_data;
    -
    941 }
    -
    942 } else {
    -
    943 return src_data;
    -
    944 }
    -
    945}
    -
    946
    -
    947static bool remove_at(qvector_t *vector, int index) {
    -
    948 if (index < 0) {
    -
    949 index += vector->num;
    -
    950 }
    -
    951 if (index >= vector->num) {
    -
    952 if (vector->num == 0) {
    -
    953 errno = ENOENT;
    -
    954 return false;
    -
    955 } else {
    -
    956 errno = ERANGE;
    -
    957 return false;
    -
    958 }
    -
    959 }
    -
    960
    -
    961 int i;
    -
    962 for (i = index + 1; i < vector->num; i++) {
    -
    963 void *src = (unsigned char *)vector->data + i * vector->objsize;
    -
    964 void *dst = (unsigned char *)vector->data + (i - 1) * vector->objsize;
    -
    965
    -
    966 memcpy(dst, src, vector->objsize);
    -
    967 }
    -
    968
    -
    969 return true;
    -
    970}
    -
    971
    -
    972#endif
    -
    bool qvector_addfirst(qvector_t *vector, const void *data)
    qvector->addfirst(): Insert a element at the beginning of this vector.
    Definition qvector.c:233
    -
    bool qvector_removefirst(qvector_t *vector)
    qvector->removefirst(): Removes the first element in this vector.
    Definition qvector.c:596
    -
    void qvector_reverse(qvector_t *vector)
    qvector->reverse(): Reverse the order of element in this vector.
    Definition qvector.c:823
    -
    void * qvector_getat(qvector_t *vector, int index, bool newmem)
    qvector->getat(): Returns the element at the specified position in this vector.
    Definition qvector.c:423
    -
    void * qvector_poplast(qvector_t *vector)
    qvector->poplast(): Returns the last element of this vector.
    Definition qvector.c:539
    -
    bool qvector_debug(qvector_t *vector, FILE *out)
    qvector->debug(): Prints out stored elements for debugging purpose.
    Definition qvector.c:707
    -
    void qvector_lock(qvector_t *vector)
    qvector->lock(): Enters critical section.
    Definition qvector.c:657
    -
    bool qvector_resize(qvector_t *vector, size_t newmax)
    qvector->resize(): Changes the allocated memory space size.
    Definition qvector.c:746
    -
    void * qvector_getfirst(qvector_t *vector, bool newmem)
    qvector->addat(): Inserts a element at the specified position in this vector.
    Definition qvector.c:371
    -
    void * qvector_popfirst(qvector_t *vector)
    qvector->popfirst(): Returns and remove the first element in this vector.
    Definition qvector.c:525
    -
    void * qvector_getlast(qvector_t *vector, bool newmem)
    qvector->getlast(): Returns the last element in this vector.
    Definition qvector.c:395
    -
    void qvector_free(qvector_t *vector)
    qvector->free(): Free this vector.
    Definition qvector.c:686
    -
    void qvector_clear(qvector_t *vector)
    qvector->clear(): Remove all the elemnts in this vector.
    Definition qvector.c:675
    -
    qvector_t * qvector(size_t max, size_t objsize, int options)
    Create new qvector_t container.
    Definition qvector.c:117
    -
    void qvector_unlock(qvector_t *vector)
    qvector->unlock(): Leaves critical section.
    Definition qvector.c:666
    -
    void * qvector_toarray(qvector_t *vector, size_t *size)
    qvector->toarray(): Returns an array contains all the elements in this vector.
    Definition qvector.c:787
    -
    bool qvector_setlast(qvector_t *vector, const void *data)
    qvector->setlast(): Set the last element with a new value in this vector.
    Definition qvector.c:477
    -
    bool qvector_getnext(qvector_t *vector, qvector_obj_t *obj, bool newmem)
    qvector->getnext(): Get next element in this vector.
    Definition qvector.c:881
    -
    bool qvector_setfirst(qvector_t *vector, const void *data)
    qvector->setfirst(): Set the first element with a new value in this vector.
    Definition qvector.c:452
    -
    bool qvector_removelast(qvector_t *vector)
    qvector->removelast(): Removes the last element in this vector.
    Definition qvector.c:610
    -
    bool qvector_removeat(qvector_t *vector, int index)
    qvector->removeat(): Removes the element at the specified position in this vector.
    Definition qvector.c:625
    -
    void * qvector_popat(qvector_t *vector, int index)
    qvector->popat(): Returns and remove the element at specified position in this vector.
    Definition qvector.c:567
    -
    bool qvector_setat(qvector_t *vector, int index, const void *data)
    qvector->setat(): Set new value to the specified position in this vector.
    Definition qvector.c:503
    -
    size_t qvector_size(qvector_t *vector)
    qvector->size(): Get the number of elements in this vector.
    Definition qvector.c:644
    -
    bool qvector_addlast(qvector_t *vector, const void *data)
    qvector->addlast(): Insert a element at the end of this vector.
    Definition qvector.c:259
    +Go to the documentation of this file.
    1 /******************************************************************************
    +
    2  * qLibc
    +
    3  *
    +
    4  * Copyright (c) 2010-2015 Seungyoung Kim.
    +
    5  * All rights reserved.
    +
    6  *
    +
    7  * Redistribution and use in source and binary forms, with or without
    +
    8  * modification, are permitted provided that the following conditions are met:
    +
    9  *
    +
    10  * 1. Redistributions of source code must retain the above copyright notice,
    +
    11  * this list of conditions and the following disclaimer.
    +
    12  * 2. Redistributions in binary form must reproduce the above copyright notice,
    +
    13  * this list of conditions and the following disclaimer in the documentation
    +
    14  * and/or other materials provided with the distribution.
    +
    15  *
    +
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    +
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    +
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    +
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    +
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    +
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    +
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    +
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    +
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    +
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    +
    26  * POSSIBILITY OF SUCH DAMAGE.
    +
    27  *****************************************************************************/
    +
    28 /* This code is written and updated by following people and released under
    +
    29  * the same license as above qLibc license.
    +
    30  * Copyright (c) 2015 Zhenjiang Xie - https://github.com/Charles0429
    +
    31  *****************************************************************************/
    +
    32 
    +
    33 /**
    +
    34  * @file qvector.c Vector container implementation.
    +
    35  *
    +
    36  * qvector container is a vector container implementation
    +
    37  * qvector provides uniformly named methods to add, get, pop and remove an
    +
    38  * element at the beginning and end of the vector.
    +
    39  *
    +
    40  * @code
    +
    41  * [Conceptional Data Structure Diagram]
    +
    42  *
    +
    43  * DATA [ C ][ B ][ A ]
    +
    44  * (positive index) 0 1 2
    +
    45  * (negative index) -3 -2 -1
    +
    46  *
    +
    47  * @encode
    +
    48  *
    +
    49  * @code
    +
    50  * //create a vector
    +
    51  * qvector_t *vector = qvector(QVECTOR_THREADSAFE, 3, sizeof(int));
    +
    52  *
    +
    53  * //insert elements
    +
    54  * vector->addlast(vector, 100);
    +
    55  * vector->addlast(vector, 101);
    +
    56  * vector->addlast(vector, 102);
    +
    57  *
    +
    58  * //get
    +
    59  * void *e1 = vector->getfirst(vector, true); //malloced
    +
    60  * void *e3 = vector->getlast(vector, false); //not malloced
    +
    61  * (...omit...)
    +
    62  * free(e1);
    +
    63  *
    +
    64  * //pop (get and remove)
    +
    65  * void *e2 = vector->popat(vector, 1); //get malloced copy
    +
    66  * (...omit...)
    +
    67  * free(e2);
    +
    68  *
    +
    69  * //debug output
    +
    70  * vector->debug(vector, stdout, true);
    +
    71  *
    +
    72  * //remove all the elements
    +
    73  * vector->clear(vector);
    +
    74  *
    +
    75  * //free vector object
    +
    76  * vector->free(vector);
    +
    77  * @endcode
    +
    78  */
    +
    79 
    +
    80 #include <stdio.h>
    +
    81 #include <string.h>
    +
    82 #include <errno.h>
    +
    83 #include <stdbool.h>
    +
    84 #include "qinternal.h"
    +
    85 #include "containers/qvector.h"
    +
    86 
    +
    87 #ifndef _DOXGEN_SKIP
    +
    88 
    +
    89 static void *get_at(qvector_t *vector, int index, bool newmem);
    +
    90 static bool remove_at(qvector_t *vector, int index);
    +
    91 
    +
    92 #endif
    +
    93 
    +
    94 /**
    +
    95  * Create new qvector_t container
    +
    96  *
    +
    97  * @param max max number of elements
    +
    98  * @param objsize size of each element
    +
    99  * @param options combination of initialization options.
    +
    100  *
    +
    101  * @return a pointer of malloced qvector_t container, otherwise returns NULL
    +
    102  * @retval errno will be set in error condition.
    +
    103  * - ENOMEM : Memory allocation failure.
    +
    104  * - EINVAL : Invalid argument.
    +
    105  *
    +
    106  * @code
    +
    107  * qvector_t *vector = qvector(10, sizeof(int), 0);
    +
    108  * @endcode
    +
    109  *
    +
    110  * @note
    +
    111  * Available options:
    +
    112  * - QVECTOR_THREADSAFE - make it thread-safe.
    +
    113  * - QVECTOR_RESIZE_DOUBLE - double the size when vector is full
    +
    114  * - QVECTOR_RESIZE_LINEAR - add the size with initial num when vector is full
    +
    115  * - QVECTOR_RESIZE_EXACT - add up as much as needed
    +
    116  */
    +
    117 qvector_t *qvector(size_t max, size_t objsize, int options) {
    +
    118  if (objsize == 0) {
    +
    119  errno = EINVAL;
    +
    120  return NULL;
    +
    121  }
    +
    122 
    +
    123  qvector_t *vector = (qvector_t *)calloc(1, sizeof(qvector_t));
    +
    124  if (vector == NULL) {
    +
    125  errno = ENOMEM;
    +
    126  return NULL;
    +
    127  }
    +
    128 
    +
    129  if (max == 0) {
    +
    130  vector->data = NULL;
    +
    131  vector->num = 0;
    +
    132  vector->max = 0;
    +
    133  vector->objsize = objsize;
    +
    134  } else {
    +
    135  void *data = malloc(max * objsize);
    +
    136  if (data == NULL) {
    +
    137  free(vector);
    +
    138  errno = ENOMEM;
    +
    139  return NULL;
    +
    140  }
    +
    141 
    +
    142  vector->data = data;
    +
    143  vector->num = 0;
    +
    144  vector->objsize = objsize;
    +
    145  vector->max = max;
    +
    146  }
    +
    147 
    +
    148  //handle options
    +
    149  if (options & QVECTOR_THREADSAFE) {
    +
    150  Q_MUTEX_NEW(vector->qmutex, true);
    +
    151  if (vector->qmutex == NULL) {
    +
    152  free(vector);
    +
    153  errno = ENOMEM;
    +
    154  return NULL;
    +
    155  }
    +
    156  }
    +
    157 
    +
    158  vector->options = 0;
    +
    159  if (options & QVECTOR_RESIZE_DOUBLE) {
    +
    160  vector->options |= QVECTOR_RESIZE_DOUBLE;
    +
    161  } else if (options & QVECTOR_RESIZE_LINEAR) {
    +
    162  vector->options |= QVECTOR_RESIZE_LINEAR;
    +
    163  if (max == 0) {
    +
    164  vector->initnum = 1;
    +
    165  } else {
    +
    166  vector->initnum = max;
    +
    167  }
    +
    168  } else {
    +
    169  vector->options |= QVECTOR_RESIZE_EXACT;
    +
    170  }
    +
    171 
    +
    172  //member methods
    +
    173  vector->addfirst = qvector_addfirst;
    +
    174  vector->addlast = qvector_addlast;
    +
    175  vector->addat = qvector_addat;
    +
    176 
    +
    177  vector->getfirst = qvector_getfirst;
    +
    178  vector->getlast = qvector_getlast;
    +
    179  vector->getat = qvector_getat;
    +
    180 
    +
    181  vector->setfirst = qvector_setfirst;
    +
    182  vector->setlast = qvector_setlast;
    +
    183  vector->setat = qvector_setat;
    +
    184 
    +
    185  vector->popfirst = qvector_popfirst;
    +
    186  vector->poplast = qvector_poplast;
    +
    187  vector->popat = qvector_popat;
    +
    188 
    +
    189  vector->removefirst = qvector_removefirst;
    +
    190  vector->removelast = qvector_removelast;
    +
    191  vector->removeat = qvector_removeat;
    +
    192 
    +
    193  vector->size = qvector_size;
    +
    194  vector->resize = qvector_resize;
    +
    195 
    +
    196  vector->toarray = qvector_toarray;
    +
    197 
    +
    198  vector->lock = qvector_lock;
    +
    199  vector->unlock = qvector_unlock;
    +
    200 
    +
    201  vector->clear = qvector_clear;
    +
    202  vector->debug = qvector_debug;
    +
    203  vector->free = qvector_free;
    +
    204 
    +
    205  vector->reverse = qvector_reverse;
    +
    206  vector->getnext = qvector_getnext;
    +
    207 
    +
    208  return vector;
    +
    209 }
    +
    210 
    +
    211 /**
    +
    212  * qvector->addfirst(): Insert a element at the beginning of this vector.
    +
    213  *
    +
    214  * @param vector qvector_t container pointer.
    +
    215  * @param data a pointer which points data memory
    +
    216  *
    +
    217  * @return true if successful, otherwise returns false.
    +
    218  * @retval errno will be set in error condition.
    +
    219  *
    +
    220  * - EINVAL : Invalid argument.
    +
    221  * - ENOMEM : Memory allocation failure.
    +
    222  *
    +
    223  * @code
    +
    224  * //create a sample object.
    +
    225  * struct my_obj obj;
    +
    226  *
    +
    227  * //create a vector and add the sample object.
    +
    228  * qvector_t *vector = qvector(0, 1, sizeof(struct my_obj));
    +
    229  * vector->addfirst(vector, &obj);
    +
    230  *
    +
    231  * @endcode
    +
    232  */
    +
    233 bool qvector_addfirst(qvector_t *vector, const void *data) {
    +
    234  return vector->addat(vector, 0, data);
    +
    235 }
    +
    236 
    +
    237 /**
    +
    238  * qvector->addlast(): Insert a element at the end of this vector.
    +
    239  *
    +
    240  * @param vector qvector_t container pointer.
    +
    241  * @param data a pointer which points data memory
    +
    242  *
    +
    243  * @return true if successful, otherwise returns false.
    +
    244  * @retval errno will be set in error condition.
    +
    245  *
    +
    246  * - EINVAL : Invalid argument.
    +
    247  * - ENOMEM : Memory allocation failure.
    +
    248  *
    +
    249  * @code
    +
    250  * //create a sample object.
    +
    251  * struct my_obj obj;
    +
    252  *
    +
    253  * //create a vector and add the sample object.
    +
    254  * qvector_t *vector = qvector(0, 1, sizeof(struct my_obj));
    +
    255  * vector->addlast(vector, &obj);
    +
    256  *
    +
    257  * @endcode
    +
    258  */
    +
    259 bool qvector_addlast(qvector_t *vector, const void *data) {
    +
    260  return vector->addat(vector, vector->num, data);
    +
    261 }
    +
    262 
    +
    263 /**
    +
    264  * qvector->addat(): Inserts a element at the specified position in this
    +
    265  * vector.
    +
    266  *
    +
    267  * @param vector qvector_t container pointer
    +
    268  * @param index index at which the specified element is to be inserted
    +
    269  * @param data a pointer which points data memory
    +
    270  *
    +
    271  * @return true if successful, otherwise returns false.
    +
    272  * @retval errno will be set in errno condition.
    +
    273  *
    +
    274  * - ERANGE : Index out of range.
    +
    275  * - EINVAL : Invalid argument.
    +
    276  * - ENOMEM : Memory allocation failure.
    +
    277  *
    +
    278  * @code
    +
    279  * first last new
    +
    280  * Array [ A ][ B ][ C ]?==?[ ]
    +
    281  * (positive index) 0 1 2 3
    +
    282  * (negative index) -3 -2 -1
    +
    283  *
    +
    284  * @encode
    +
    285  *
    +
    286  * @code
    +
    287  * qvector_t *vector = qvector();
    +
    288  * vector->addat(vector, 0, &data); //same as addfirst().
    +
    289  * vector->addat(vector, 0, &data); //same as addlast().
    +
    290  *
    +
    291  * @encode
    +
    292  *
    +
    293  * @note
    +
    294  * Index starts from 0.
    +
    295  */
    +
    296 bool qvector_addat(qvector_t *vector, int index, const void *data) {
    +
    297  //check arguments
    +
    298  if (data == NULL) {
    +
    299  errno = EINVAL;
    +
    300  return false;
    +
    301  }
    +
    302 
    +
    303  //check index
    +
    304  if (index < 0) {
    +
    305  index += vector->num;
    +
    306  }
    +
    307  if (index > vector->num) {
    +
    308  errno = ERANGE;
    +
    309  return false;
    +
    310  }
    +
    311 
    +
    312  vector->lock(vector);
    +
    313 
    +
    314  //check whether the vector is full
    +
    315  if (vector->num >= vector->max) {
    +
    316  size_t newmax = vector->max + 1;
    +
    317  if (vector->options & QVECTOR_RESIZE_DOUBLE) {
    +
    318  newmax = (vector->max + 1) * 2;
    +
    319  } else if (vector->options & QVECTOR_RESIZE_LINEAR) {
    +
    320  newmax = vector->max + vector->initnum;
    +
    321  } else {
    +
    322  newmax = vector->max + 1;
    +
    323  }
    +
    324  bool result = vector->resize(vector, newmax);
    +
    325  if (result == false)
    +
    326  {
    +
    327  vector->unlock(vector);
    +
    328  errno = ENOMEM;
    +
    329  return false;
    +
    330  }
    +
    331  }
    +
    332 
    +
    333  //shift data from index...(num - 1) to index + 1...num
    +
    334  int i;
    +
    335  for (i = vector->num; i > index; i--) {
    +
    336  void *dst = (unsigned char *)vector->data + vector->objsize * i;
    +
    337  void *src = (unsigned char *)vector->data + vector->objsize * (i - 1);
    +
    338 
    +
    339  memcpy(dst, src, vector->objsize);
    +
    340  }
    +
    341 
    +
    342  void *add = (unsigned char *)vector->data + index * vector->objsize;
    +
    343  memcpy(add, data, vector->objsize);
    +
    344  vector->num++;
    +
    345 
    +
    346  vector->unlock(vector);
    +
    347  return true;
    +
    348 }
    +
    349 
    +
    350 /**
    +
    351  * qvector->getfirst(): Returns the first element in this vector.
    +
    352  *
    +
    353  * @param vector qvector_t container pointer.
    +
    354  * @param newmem whether or not to allocate memory for the element.
    +
    355  *
    +
    356  * @return a pointer of element, otherwise returns NULL.
    +
    357  * @retval errno will be set in error condition.
    +
    358  * - ENOENT : Vector is empty.
    +
    359  * - ENOMEM : Memory allocation failure.
    +
    360  *
    +
    361  * @code
    +
    362  * size_t size;
    +
    363  * void *data = vector->getfirst(vector, true);
    +
    364  * if (data != NULL) {
    +
    365  * (...omit...)
    +
    366  * free(data);
    +
    367  * }
    +
    368  *
    +
    369  * @endcode
    +
    370  */
    +
    371 void *qvector_getfirst(qvector_t *vector, bool newmem) {
    +
    372  return vector->getat(vector, 0, newmem);
    +
    373 }
    +
    374 
    +
    375 /**
    +
    376  * qvector->getlast(): Returns the last element in this vector.
    +
    377  *
    +
    378  * @param vector qvector_t container pointer.
    +
    379  * @param newmem whether or not to allocate memory for the element.
    +
    380  *
    +
    381  * @return a pointer of element, otherwise returns NULL.
    +
    382  * @retval errno will be set in error condition.
    +
    383  * - ENOENT : Vector is empty.
    +
    384  * - ENOMEM : Memory alocation failure.
    +
    385  *
    +
    386  * @code
    +
    387  * void *data = vector->getlast(vector, true);
    +
    388  * if (data != NULL) {
    +
    389  * (...omit...)
    +
    390  * free(data);
    +
    391  * }
    +
    392  *
    +
    393  * @endcode
    +
    394  */
    +
    395 void *qvector_getlast(qvector_t *vector, bool newmem) {
    +
    396  return vector->getat(vector, -1, newmem);
    +
    397 }
    +
    398 
    +
    399 /**
    +
    400  * qvector->getat(): Returns the element at the specified position in this
    +
    401  * vector.
    +
    402  *
    +
    403  * @param vector qvector_t container pointer.
    +
    404  * @param index index at which the specified element is to access.
    +
    405  * @param newmem whether or not to allocate memory for the element.
    +
    406  *
    +
    407  * @return a pointer of element, otherwise returns NULL.
    +
    408  * @retval errno will be set in error condition.
    +
    409  * - ERANGE : Index out of range.
    +
    410  * - ENOMEM : Memory allocation failure.
    +
    411  *
    +
    412  * @code
    +
    413  * first last
    +
    414  * Array [ A ][ B ][ C ]
    +
    415  * (positive index) 0 1 2
    +
    416  * (negative index) -1 -2 -3
    +
    417  *
    +
    418  * @endcode
    +
    419  *
    +
    420  * @note
    +
    421  * Index starts from 0.
    +
    422  */
    +
    423 void *qvector_getat(qvector_t *vector, int index, bool newmem) {
    +
    424  vector->lock(vector);
    +
    425  void *data = get_at(vector, index, newmem);
    +
    426  vector->unlock(vector);
    +
    427 
    +
    428  return data;
    +
    429 }
    +
    430 
    +
    431 /**
    +
    432  * qvector->setfirst(): Set the first element with a new value in this
    +
    433  * vector.
    +
    434  *
    +
    435  * @param vector qvector_t container pointer.
    +
    436  * @param data the pointer of new value.
    +
    437  *
    +
    438  * @returns true if successful, otherwise returns false.
    +
    439  * @retval errno will be set in error condition.
    +
    440  * - ENOENT : Vector is empty.
    +
    441  *
    +
    442  * @code
    +
    443  *
    +
    444  * struct my_obj obj;
    +
    445  * //set values to obj;
    +
    446  * qvector_t *vector = qvector();
    +
    447  * vector->addlast();
    +
    448  * vector->setfirst(vector, &obj);
    +
    449  *
    +
    450  * @endcode
    +
    451  */
    +
    452 bool qvector_setfirst(qvector_t *vector, const void *data) {
    +
    453  return vector->setat(vector, 0, data);
    +
    454 }
    +
    455 
    +
    456 /**
    +
    457  * qvector->setlast(): Set the last element with a new value in this
    +
    458  * vector.
    +
    459  *
    +
    460  * @param vector qvector_t container pointer.
    +
    461  * @param data the pointer of new value.
    +
    462  *
    +
    463  * @returns true if successful, otherwise returns false.
    +
    464  * @retval errno will be set in error condition.
    +
    465  * - ENOENT : Vector is empty.
    +
    466  *
    +
    467  * @code
    +
    468  *
    +
    469  * struct my_obj obj;
    +
    470  * //set values to obj;
    +
    471  * qvector_t *vector = qvector();
    +
    472  * vector->addlast();
    +
    473  * vector->setlast(vector, &obj);
    +
    474  *
    +
    475  * @endcode
    +
    476  */
    +
    477 bool qvector_setlast(qvector_t *vector, const void *data) {
    +
    478  return vector->setat(vector, -1, data);
    +
    479 }
    +
    480 
    +
    481 /**
    +
    482  * qvector->setat(): Set new value to the specified position in this
    +
    483  * vector.
    +
    484  *
    +
    485  * @param vector qvector_t container pointer
    +
    486  * @param index index at which the specifed element is to set
    +
    487  * @param data the pointer of new value to be set
    +
    488  *
    +
    489  * @return true if successful, otherwise return false.
    +
    490  * @retval errno will be set in error condition.
    +
    491  * - ERANGE : Index out of range.
    +
    492  *
    +
    493  * @code
    +
    494  *
    +
    495  * struct my_obj obj;
    +
    496  * //set values to obj;
    +
    497  * qvector_t *vector = qvector();
    +
    498  * vector->addlast();
    +
    499  * vector->setat(vector, 0, &obj);
    +
    500  *
    +
    501  * @endcode
    +
    502  */
    +
    503 bool qvector_setat(qvector_t *vector, int index, const void *data) {
    +
    504  vector->lock(vector);
    +
    505  void *old_data = get_at(vector, index, false);
    +
    506  if (old_data == NULL) {
    +
    507  return false;
    +
    508  }
    +
    509  memcpy(old_data, data, vector->objsize);
    +
    510  vector->unlock(vector);
    +
    511 
    +
    512  return true;
    +
    513 }
    +
    514 
    +
    515 /**
    +
    516  * qvector->popfirst(): Returns and remove the first element in this vector.
    +
    517  *
    +
    518  * @param vector qvector_t container pointer.
    +
    519  *
    +
    520  * @return a pointer of malloced element, otherwise returns NULL.
    +
    521  * @retval errno will be set in error condition.
    +
    522  * - ENOENT : Vector is empty.
    +
    523  * - ENOMEM : Memory allocation failure.
    +
    524  */
    +
    525 void *qvector_popfirst(qvector_t *vector) {
    +
    526  return vector->popat(vector, 0);
    +
    527 }
    +
    528 
    +
    529 /**
    +
    530  * qvector->poplast(): Returns the last element of this vector.
    +
    531  *
    +
    532  * @param vector qvector_t container pointer.
    +
    533  *
    +
    534  * @return a pointer of malloced element, otherwise returns NULL.
    +
    535  * @retval errno will be set in error condition.
    +
    536  * - ENOENT : Vector is empty.
    +
    537  * - ENOMEM : Memeory allocation failure.
    +
    538  */
    +
    539 void *qvector_poplast(qvector_t *vector) {
    +
    540  return vector->popat(vector, -1);
    +
    541 }
    +
    542 
    +
    543 /**
    +
    544  * qvector->popat(): Returns and remove the element at specified
    +
    545  * position in this vector.
    +
    546  *
    +
    547  * @param vector qvector_t container pointer.
    +
    548  * @param index index at which the specified element is to be poped.
    +
    549  *
    +
    550  * @return a pointer of malloced element, otherwise returns NULL.
    +
    551  * @retval errno will be set in error condition.
    +
    552  * - ENOENT : Vector is empty.
    +
    553  * - ERANGE : Index out of range.
    +
    554  * - ENOMEM : Mmemory allocation failure.
    +
    555  *
    +
    556  * @code
    +
    557  * first last
    +
    558  * Array [ A ][ B ][ C ]
    +
    559  * (positive index) 1 2 3
    +
    560  * (negative index) -1 -2 -3
    +
    561  *
    +
    562  * @endcode
    +
    563  *
    +
    564  * @note
    +
    565  * Index starts from 0.
    +
    566  */
    +
    567 void *qvector_popat(qvector_t *vector, int index) {
    +
    568  vector->lock(vector);
    +
    569  void *data = get_at(vector, index, true);
    +
    570  if (data == NULL) {
    +
    571  return NULL;
    +
    572  }
    +
    573 
    +
    574  bool result = remove_at(vector, index);
    +
    575  if (result == false) {
    +
    576  free(data);
    +
    577  vector->unlock(vector);
    +
    578  return NULL;
    +
    579  }
    +
    580  vector->num--;
    +
    581 
    +
    582  vector->unlock(vector);
    +
    583  return data;
    +
    584 }
    +
    585 
    +
    586 /**
    +
    587  * qvector->removefirst(): Removes the first element in this vector.
    +
    588  *
    +
    589  * @param vector qvector_t container pointer.
    +
    590  *
    +
    591  * @return true, otherwise returns false.
    +
    592  * @retval errno will be set in error condition.
    +
    593  * - ENOENT : Vector is empty.
    +
    594  * - ERANGE : Index out of range.
    +
    595  */
    +
    596 bool qvector_removefirst(qvector_t *vector) {
    +
    597  return vector->removeat(vector, 0);
    +
    598 }
    +
    599 
    +
    600 /**
    +
    601  * qvector->removelast(): Removes the last element in this vector.
    +
    602  *
    +
    603  * @param vector qvector_t container pointer.
    +
    604  *
    +
    605  * @return true, otherwise returns false.
    +
    606  * @retval errno will be set in error condition.
    +
    607  * - ENOENT : Vector is empty.
    +
    608  * - ERANGE : Index out of range.
    +
    609  */
    +
    610 bool qvector_removelast(qvector_t *vector) {
    +
    611  return vector->removeat(vector, -1);
    +
    612 }
    +
    613 
    +
    614 /**
    +
    615  * qvector->removeat(): Removes the element at the specified position in this vector.
    +
    616  *
    +
    617  * @param vector qvector_t container pointer.
    +
    618  * @param index index at which the specified element is to be removed.
    +
    619  *
    +
    620  * @return true, otherwise returns false.
    +
    621  * @retval errno will be set in error condition.
    +
    622  * - ENOENT : Vector is empty.
    +
    623  * - ERANGE : Index out of range.
    +
    624  */
    +
    625 bool qvector_removeat(qvector_t *vector, int index) {
    +
    626  vector->lock(vector);
    +
    627  bool result = remove_at(vector, index);
    +
    628  if (result) {
    +
    629  vector->num--;
    +
    630  }
    +
    631 
    +
    632  vector->unlock(vector);
    +
    633 
    +
    634  return result;
    +
    635 }
    +
    636 
    +
    637 /**
    +
    638  * qvector->size(): Get the number of elements in this vector.
    +
    639  *
    +
    640  * @param vector qvector_t container pointer.
    +
    641  *
    +
    642  * @return the number of elements in this vector.
    +
    643  */
    +
    644 size_t qvector_size(qvector_t *vector) {
    +
    645  return vector->num;
    +
    646 }
    +
    647 
    +
    648 /**
    +
    649  * qvector->lock(): Enters critical section.
    +
    650  *
    +
    651  * @param vector qvector_t container pointer.
    +
    652  *
    +
    653  * @note
    +
    654  * From user side, normally locking operation is only needed when traverse all
    +
    655  * elements using qvector->getnext().
    +
    656  */
    +
    657 void qvector_lock(qvector_t *vector) {
    +
    658  Q_MUTEX_ENTER(vector->qmutex);
    +
    659 }
    +
    660 
    +
    661 /**
    +
    662  * qvector->unlock(): Leaves critical section.
    +
    663  *
    +
    664  * @param vector qvector_t container pointer.
    +
    665  */
    +
    666 void qvector_unlock(qvector_t *vector) {
    +
    667  Q_MUTEX_LEAVE(vector->qmutex);
    +
    668 }
    +
    669 
    +
    670 /**
    +
    671  * qvector->clear(): Remove all the elemnts in this vector.
    +
    672  *
    +
    673  * @param vector qvector_t container pointer.
    +
    674  */
    +
    675 void qvector_clear(qvector_t *vector) {
    +
    676  vector->lock(vector);
    +
    677  vector->num = 0;
    +
    678  vector->unlock(vector);
    +
    679 }
    +
    680 
    +
    681 /**
    +
    682  * qvector->free(): Free this vector.
    +
    683  *
    +
    684  * @param vector qvector_t container pointer.
    +
    685  */
    +
    686 void qvector_free(qvector_t *vector) {
    +
    687  vector->clear(vector);
    +
    688  Q_MUTEX_DESTROY(vector->qmutex);
    +
    689 
    +
    690  if (vector->data != NULL) {
    +
    691  free(vector->data);
    +
    692  }
    +
    693 
    +
    694  free(vector);
    +
    695 }
    +
    696 
    +
    697 /**
    +
    698  * qvector->debug(): Prints out stored elements for debugging purpose.
    +
    699  *
    +
    700  * @param vector qvector_t container pointer.
    +
    701  * @param out output stream FILE descriptor such like stdout, stderr.
    +
    702  *
    +
    703  * @return true if successful, otherwise returns false.
    +
    704  * @retval errno will be set in error condition.
    +
    705  * - EIO : Invalid output stream.
    +
    706  */
    +
    707 bool qvector_debug(qvector_t *vector, FILE *out) {
    +
    708  if (out == NULL) {
    +
    709  errno = EIO;
    +
    710  return false;
    +
    711  }
    +
    712 
    +
    713  vector->lock(vector);
    +
    714  int i;
    +
    715  for (i = 0; i < vector->num; i++) {
    +
    716  void *data = (unsigned char *)vector->data + i * vector->objsize;
    +
    717  fprintf(out, "%d=", i);
    +
    718  _q_textout(out, data, vector->objsize, MAX_HUMANOUT);
    +
    719  fprintf(out, " (%zu)\n", vector->objsize);
    +
    720  }
    +
    721  vector->unlock(vector);
    +
    722 
    +
    723  return true;
    +
    724 }
    +
    725 
    +
    726 /**
    +
    727  * qvector->resize(): Changes the allocated memory space size.
    +
    728  *
    +
    729  * @param vector qvector_t container pointer.
    +
    730  * @param newsize the new max number of elements.
    +
    731  *
    +
    732  * @retval errno will be set in error condition.
    +
    733  * - ENOMEM : Memory allocation failure.
    +
    734  *
    +
    735  * @code
    +
    736  * //create a sample object.
    +
    737  * struct my_obj obj;
    +
    738  *
    +
    739  * //create a vector which allocates 4 * sizeof(obj) memory
    +
    740  * qvector_t *vector = qvector(0, 4, sizeof(struct my_obj));
    +
    741  * //expand the memory space of vector to 8 * sizeof(obj)
    +
    742  * vector->resize(vector, 8);
    +
    743  *
    +
    744  * @endcode
    +
    745  */
    +
    746 bool qvector_resize(qvector_t *vector, size_t newmax) {
    +
    747  vector->lock(vector);
    +
    748 
    +
    749  if (newmax == 0) {
    +
    750  free(vector->data);
    +
    751  vector->data = NULL;
    +
    752  vector->max = 0;
    +
    753  vector->num = 0;
    +
    754  vector->objsize = 0;
    +
    755 
    +
    756  vector->unlock(vector);
    +
    757  return true;
    +
    758  }
    +
    759 
    +
    760  void *newdata = realloc(vector->data, newmax * vector->objsize);
    +
    761  if (newdata == NULL) {
    +
    762  errno = ENOMEM;
    +
    763  vector->unlock(vector);
    +
    764  return false;
    +
    765  }
    +
    766 
    +
    767  vector->data = newdata;
    +
    768  vector->max = newmax;
    +
    769  if (vector->num > newmax) {
    +
    770  vector->num = newmax;
    +
    771  }
    +
    772 
    +
    773  vector->unlock(vector);
    +
    774  return true;
    +
    775 }
    +
    776 
    +
    777 /**
    +
    778  * qvector->toarray(): Returns an array contains all the elements in this vector.
    +
    779  * @param vector qvector_t container pointer.
    +
    780  * @param size if size is not NULL, the number of elements will be stored.
    +
    781  *
    +
    782  * @return a malloced pointer, otherwise return NULL.
    +
    783  * @retval errno wil be set in error condition.
    +
    784  * - ENOENT : Vector is empty.
    +
    785  * - ENOMEM : Memory allocation failure.
    +
    786  */
    +
    787 void *qvector_toarray(qvector_t *vector, size_t *size) {
    +
    788  if (vector->num <= 0) {
    +
    789  if (size != NULL) {
    +
    790  *size = 0;
    +
    791  }
    +
    792  errno = ENOENT;
    +
    793  return NULL;
    +
    794  }
    +
    795 
    +
    796  vector->lock(vector);
    +
    797 
    +
    798  void *array = malloc(vector->num * vector->objsize);
    +
    799  if (array == NULL) {
    +
    800  vector->unlock(vector);
    +
    801  errno = ENOMEM;
    +
    802  return NULL;
    +
    803  }
    +
    804 
    +
    805  memcpy(array, vector->data, vector->num * vector->objsize);
    +
    806 
    +
    807  if (size != NULL) {
    +
    808  *size = vector->num;
    +
    809  }
    +
    810 
    +
    811  vector->unlock(vector);
    +
    812  return array;
    +
    813 }
    +
    814 
    +
    815 /**
    +
    816  * qvector->reverse(): Reverse the order of element in this vector.
    +
    817  *
    +
    818  * @param vector qvector_t container pointer.
    +
    819  *
    +
    820  * @retval will be set in error condition.
    +
    821  * - ENOMEM : Memory allocations failure.
    +
    822  */
    +
    823 void qvector_reverse(qvector_t *vector) {
    +
    824  vector->lock(vector);
    +
    825 
    +
    826  if (vector->num <= 1) {
    +
    827  vector->unlock(vector);
    +
    828  return;
    +
    829  }
    +
    830 
    +
    831  int i;
    +
    832  int j;
    +
    833  void *tmp = malloc(vector->objsize);
    +
    834  if (tmp == NULL) {
    +
    835  errno = ENOMEM;
    +
    836  return;
    +
    837  }
    +
    838 
    +
    839  for (i = 0, j = vector->num - 1; i < j; i++, j--) {
    +
    840  void *data1 = (unsigned char *)vector->data + i * vector->objsize;
    +
    841  void *data2 = (unsigned char *)vector->data + j * vector->objsize;
    +
    842 
    +
    843  memcpy(tmp, data1, vector->objsize);
    +
    844  memcpy(data1, data2, vector->objsize);
    +
    845  memcpy(data2, tmp, vector->objsize);
    +
    846  }
    +
    847  free(tmp);
    +
    848 
    +
    849  vector->unlock(vector);
    +
    850 }
    +
    851 
    +
    852 /**
    +
    853  * qvector->getnext(): Get next element in this vector.
    +
    854  *
    +
    855  * @param vector qvector_t container pointer.
    +
    856  * @param obj found data will be stored in this structure.
    +
    857  * @param newmem whether or not to allocate memory for element.
    +
    858  *
    +
    859  * @return true if found, otherwise return fasle.
    +
    860  * @retval errno will be set in error condition.
    +
    861  * - ENOENT : No next element.
    +
    862  * - ENOMEM : Memory allocation failure.
    +
    863  *
    +
    864  * @note
    +
    865  * obj should be initialized with 0 by using memset() by the first call.
    +
    866  * If newmem flag is true, user should de-allocate obj.data resources.
    +
    867  *
    +
    868  * @code
    +
    869  * qvector_t *vector = qvector();
    +
    870  * (...add data into vector...)
    +
    871  *
    +
    872  * qvector_obj_t obj;
    +
    873  * memset((void *)&obj, 0, sizeof(obj));
    +
    874  * vector->lock(vector);
    +
    875  * while(vector->getnext(vector, &obj, false) == true) {
    +
    876  * printf("DATA=%s\n", obj.data);
    +
    877  * }
    +
    878  * vector->unlock(vector);
    +
    879  * @endcode
    +
    880  */
    +
    881 bool qvector_getnext(qvector_t *vector, qvector_obj_t *obj, bool newmem) {
    +
    882  if (obj == NULL) {
    +
    883  return false;
    +
    884  }
    +
    885  vector->lock(vector);
    +
    886 
    +
    887  if (obj->index >= vector->num) {
    +
    888  errno = ENOENT;
    +
    889  obj->data = NULL;
    +
    890  vector->unlock(vector);
    +
    891  return false;
    +
    892  }
    +
    893 
    +
    894  void *data = (unsigned char *)vector->data + (obj->index) * vector->objsize;
    +
    895  if (newmem) {
    +
    896  void *dump = malloc(vector->objsize);
    +
    897  if (dump == NULL ) {
    +
    898  errno = ENOMEM;
    +
    899  obj->data = NULL;
    +
    900  vector->unlock(vector);
    +
    901  return false;
    +
    902  }
    +
    903  memcpy(dump, data, vector->objsize);
    +
    904  obj->data = dump;
    +
    905  }
    +
    906  else
    +
    907  {
    +
    908  obj->data = data;
    +
    909  }
    +
    910 
    +
    911  obj->index++;
    +
    912  vector->unlock(vector);
    +
    913  return true;
    +
    914 }
    +
    915 
    +
    916 #ifndef _DOXYGEN_SKIP
    +
    917 
    +
    918 static void *get_at(qvector_t *vector, int index, bool newmem) {
    +
    919  if (index < 0) {
    +
    920  index += vector->num;
    +
    921  }
    +
    922  if (index >= vector->num) {
    +
    923  if (vector->num == 0) {
    +
    924  errno = ENOENT;
    +
    925  return NULL;
    +
    926  } else {
    +
    927  errno = ERANGE;
    +
    928  return NULL;
    +
    929  }
    +
    930  }
    +
    931 
    +
    932  void *src_data = (unsigned char *)vector->data + index * vector->objsize;
    +
    933  if (newmem) {
    +
    934  void *dump_data = malloc(vector->objsize);
    +
    935  if (dump_data == NULL) {
    +
    936  errno = ENOMEM;
    +
    937  return NULL;
    +
    938  } else {
    +
    939  memcpy(dump_data, src_data, vector->objsize);
    +
    940  return dump_data;
    +
    941  }
    +
    942  } else {
    +
    943  return src_data;
    +
    944  }
    +
    945 }
    +
    946 
    +
    947 static bool remove_at(qvector_t *vector, int index) {
    +
    948  if (index < 0) {
    +
    949  index += vector->num;
    +
    950  }
    +
    951  if (index >= vector->num) {
    +
    952  if (vector->num == 0) {
    +
    953  errno = ENOENT;
    +
    954  return false;
    +
    955  } else {
    +
    956  errno = ERANGE;
    +
    957  return false;
    +
    958  }
    +
    959  }
    +
    960 
    +
    961  int i;
    +
    962  for (i = index + 1; i < vector->num; i++) {
    +
    963  void *src = (unsigned char *)vector->data + i * vector->objsize;
    +
    964  void *dst = (unsigned char *)vector->data + (i - 1) * vector->objsize;
    +
    965 
    +
    966  memcpy(dst, src, vector->objsize);
    +
    967  }
    +
    968 
    +
    969  return true;
    +
    970 }
    +
    971 
    +
    972 #endif
    +
    bool qvector_addfirst(qvector_t *vector, const void *data)
    qvector->addfirst(): Insert a element at the beginning of this vector.
    Definition: qvector.c:233
    +
    bool qvector_removefirst(qvector_t *vector)
    qvector->removefirst(): Removes the first element in this vector.
    Definition: qvector.c:596
    +
    void qvector_reverse(qvector_t *vector)
    qvector->reverse(): Reverse the order of element in this vector.
    Definition: qvector.c:823
    +
    bool qvector_debug(qvector_t *vector, FILE *out)
    qvector->debug(): Prints out stored elements for debugging purpose.
    Definition: qvector.c:707
    +
    void qvector_lock(qvector_t *vector)
    qvector->lock(): Enters critical section.
    Definition: qvector.c:657
    +
    bool qvector_resize(qvector_t *vector, size_t newmax)
    qvector->resize(): Changes the allocated memory space size.
    Definition: qvector.c:746
    +
    void * qvector_popfirst(qvector_t *vector)
    qvector->popfirst(): Returns and remove the first element in this vector.
    Definition: qvector.c:525
    +
    void qvector_free(qvector_t *vector)
    qvector->free(): Free this vector.
    Definition: qvector.c:686
    +
    void qvector_clear(qvector_t *vector)
    qvector->clear(): Remove all the elemnts in this vector.
    Definition: qvector.c:675
    +
    void qvector_unlock(qvector_t *vector)
    qvector->unlock(): Leaves critical section.
    Definition: qvector.c:666
    +
    void * qvector_getat(qvector_t *vector, int index, bool newmem)
    qvector->getat(): Returns the element at the specified position in this vector.
    Definition: qvector.c:423
    +
    void * qvector_popat(qvector_t *vector, int index)
    qvector->popat(): Returns and remove the element at specified position in this vector.
    Definition: qvector.c:567
    +
    bool qvector_setlast(qvector_t *vector, const void *data)
    qvector->setlast(): Set the last element with a new value in this vector.
    Definition: qvector.c:477
    +
    bool qvector_getnext(qvector_t *vector, qvector_obj_t *obj, bool newmem)
    qvector->getnext(): Get next element in this vector.
    Definition: qvector.c:881
    +
    void * qvector_getfirst(qvector_t *vector, bool newmem)
    qvector->addat(): Inserts a element at the specified position in this vector.
    Definition: qvector.c:371
    +
    bool qvector_setfirst(qvector_t *vector, const void *data)
    qvector->setfirst(): Set the first element with a new value in this vector.
    Definition: qvector.c:452
    +
    bool qvector_removelast(qvector_t *vector)
    qvector->removelast(): Removes the last element in this vector.
    Definition: qvector.c:610
    +
    qvector_t * qvector(size_t max, size_t objsize, int options)
    Create new qvector_t container.
    Definition: qvector.c:117
    +
    void * qvector_getlast(qvector_t *vector, bool newmem)
    qvector->getlast(): Returns the last element in this vector.
    Definition: qvector.c:395
    +
    bool qvector_removeat(qvector_t *vector, int index)
    qvector->removeat(): Removes the element at the specified position in this vector.
    Definition: qvector.c:625
    +
    bool qvector_setat(qvector_t *vector, int index, const void *data)
    qvector->setat(): Set new value to the specified position in this vector.
    Definition: qvector.c:503
    +
    void * qvector_toarray(qvector_t *vector, size_t *size)
    qvector->toarray(): Returns an array contains all the elements in this vector.
    Definition: qvector.c:787
    +
    void * qvector_poplast(qvector_t *vector)
    qvector->poplast(): Returns the last element of this vector.
    Definition: qvector.c:539
    +
    size_t qvector_size(qvector_t *vector)
    qvector->size(): Get the number of elements in this vector.
    Definition: qvector.c:644
    +
    bool qvector_addlast(qvector_t *vector, const void *data)
    qvector->addlast(): Insert a element at the end of this vector.
    Definition: qvector.c:259
    diff --git a/doc/html/resize.js b/doc/html/resize.js index aaeb6fc0..e1ad0fe3 100644 --- a/doc/html/resize.js +++ b/doc/html/resize.js @@ -22,45 +22,38 @@ @licend The above is the entire license notice for the JavaScript code in this file */ -var once=1; function initResizable() { var cookie_namespace = 'doxygen'; - var sidenav,navtree,content,header,barWidth=6,desktop_vp=768,titleHeight; + var sidenav,navtree,content,header,collapsed,collapsedWidth=0,barWidth=6,desktop_vp=768,titleHeight; - function readSetting(cookie) + function readCookie(cookie) { - if (window.chrome) { - var val = localStorage.getItem(cookie_namespace+'_width'); - if (val) return val; - } else { - var myCookie = cookie_namespace+"_"+cookie+"="; - if (document.cookie) { - var index = document.cookie.indexOf(myCookie); - if (index != -1) { - var valStart = index + myCookie.length; - var valEnd = document.cookie.indexOf(";", valStart); - if (valEnd == -1) { - valEnd = document.cookie.length; - } - var val = document.cookie.substring(valStart, valEnd); - return val; + var myCookie = cookie_namespace+"_"+cookie+"="; + if (document.cookie) { + var index = document.cookie.indexOf(myCookie); + if (index != -1) { + var valStart = index + myCookie.length; + var valEnd = document.cookie.indexOf(";", valStart); + if (valEnd == -1) { + valEnd = document.cookie.length; } + var val = document.cookie.substring(valStart, valEnd); + return val; } } - return 250; + return 0; } - function writeSetting(cookie, val) + function writeCookie(cookie, val, expiration) { - if (window.chrome) { - localStorage.setItem(cookie_namespace+"_width",val); - } else { + if (val==undefined) return; + if (expiration == null) { var date = new Date(); date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week expiration = date.toGMTString(); - document.cookie = cookie_namespace + "_" + cookie + "=" + val + "; SameSite=Lax; expires=" + expiration+"; path=/"; } + document.cookie = cookie_namespace + "_" + cookie + "=" + val + "; expires=" + expiration+"; path=/"; } function resizeWidth() @@ -68,19 +61,13 @@ function initResizable() var windowWidth = $(window).width() + "px"; var sidenavWidth = $(sidenav).outerWidth(); content.css({marginLeft:parseInt(sidenavWidth)+"px"}); - if (typeof page_layout!=='undefined' && page_layout==1) { - footer.css({marginLeft:parseInt(sidenavWidth)+"px"}); - } - writeSetting('width',sidenavWidth-barWidth); + writeCookie('width',sidenavWidth-barWidth, null); } function restoreWidth(navWidth) { var windowWidth = $(window).width() + "px"; content.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); - if (typeof page_layout!=='undefined' && page_layout==1) { - footer.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); - } sidenav.css({width:navWidth + "px"}); } @@ -88,20 +75,23 @@ function initResizable() { var headerHeight = header.outerHeight(); var footerHeight = footer.outerHeight(); - var windowHeight = $(window).height(); - var contentHeight,navtreeHeight,sideNavHeight; - if (typeof page_layout==='undefined' || page_layout==0) { /* DISABLE_INDEX=NO */ - contentHeight = windowHeight - headerHeight - footerHeight; - navtreeHeight = contentHeight; - sideNavHeight = contentHeight; - } else if (page_layout==1) { /* DISABLE_INDEX=YES */ - contentHeight = windowHeight - footerHeight; - navtreeHeight = windowHeight - headerHeight; - sideNavHeight = windowHeight; + var windowHeight = $(window).height() - headerHeight - footerHeight; + content.css({height:windowHeight + "px"}); + navtree.css({height:windowHeight + "px"}); + sidenav.css({height:windowHeight + "px"}); + var width=$(window).width(); + if (width!=collapsedWidth) { + if (width=desktop_vp) { + if (!collapsed) { + collapseExpand(); + } + } else if (width>desktop_vp && collapsedWidth0) { - newWidth=0; + restoreWidth(0); + collapsed=true; } else { - var width = readSetting('width'); - newWidth = (width>250 && width<$(window).width()) ? width : 250; + var width = readCookie('width'); + if (width>200 && width<$(window).width()) { restoreWidth(width); } else { restoreWidth(200); } + collapsed=false; } - restoreWidth(newWidth); - var sidenavWidth = $(sidenav).outerWidth(); - writeSetting('width',sidenavWidth-barWidth); } header = $("#top"); @@ -138,7 +126,7 @@ function initResizable() $('#nav-sync').css({ right:'34px' }); barWidth=20; } - var width = readSetting('width'); + var width = readCookie('width'); if (width) { restoreWidth(width); } else { resizeWidth(); } resizeHeight(); var url = location.href; @@ -146,10 +134,7 @@ function initResizable() if (i>=0) window.location.hash=url.substr(i); var _preventDefault = function(evt) { evt.preventDefault(); }; $("#splitbar").bind("dragstart", _preventDefault).bind("selectstart", _preventDefault); - if (once) { - $(".ui-resizable-handle").dblclick(collapseExpand); - once=0 - } + $(".ui-resizable-handle").dblclick(collapseExpand); $(window).on('load',resizeHeight); } /* @license-end */ diff --git a/doc/html/splitbard.png b/doc/html/splitbard.png deleted file mode 100644 index 8367416d757fd7b6dc4272b6432dc75a75abd068..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^Yzz!63>-{AmhX=Jf@VhhFKy35^fiT zT~&lUj3=cDh^%3HDY9k5CEku}PHXNoNC(_$U3XPb&Q*ME25pT;2(*BOgAf<+R$lzakPG`kF31()Fx{L5Wrac|GQzjeE= zueY1`Ze{#x<8=S|`~MgGetGce)#vN&|J{Cd^tS%;tBYTo?+^d68<#n_Y_xx`J||4O V@QB{^CqU0Kc)I$ztaD0e0svEzbJzd? diff --git a/doc/html/tab_ad.png b/doc/html/tab_ad.png deleted file mode 100644 index e34850acfc24be58da6d2fd1ccc6b29cc84fe34d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QhuH;jv*C{Z|5d*H3V=pKi{In zd2jxLclDRPylmD}^l7{QOtL{vUjO{-WqItb5sQp2h-99b8^^Scr-=2mblCdZuUm?4 jzOJvgvt3{(cjKLW5(A@0qPS@<&}0TrS3j3^P6y&q2{!U5bk+Tso_B!YCpDh>v z{CM*1U8YvQRyBUHt^Ju0W_sq-?;9@_4equ-bavTs=gk796zopr0EBT&m;e9( diff --git a/doc/html/tab_sd.png b/doc/html/tab_sd.png deleted file mode 100644 index 757a565ced4730f85c833fb2547d8e199ae68f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qq7(&jv*C{Z|_!fH5o7*c=%9% zcILh!EA=pAQKdx-Cdiev=v{eg{8Ht<{e8_NAN~b=)%W>-WDCE0PyDHGemi$BoXwcK z{>e9^za6*c1ilttWw&V+U;WCPlV9{LdC~Ey%_H(qj`xgfES(4Yz5jSTZfCt`4E$0YRsR*S^mTCR^;V&sxC8{l_Cp7w8-YPgg&ebxsLQ00$vXK>z>% diff --git a/doc/html/tabs.css b/doc/html/tabs.css index 71c8a470..7d45d36c 100644 --- a/doc/html/tabs.css +++ b/doc/html/tabs.css @@ -1 +1 @@ -.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:var(--nav-menu-button-color);-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:var(--nav-gradient-image)}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:var(--font-family-nav);font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:var(--nav-text-normal-shadow);color:var(--nav-text-normal-color);outline:0}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:var(--nav-menu-toggle-color);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:var(--nav-menu-background-color)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:var(--nav-menu-background-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:var(--nav-gradient-image);line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:var(--nav-text-normal-color) transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:var(--nav-separator-image);background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a:hover span.sub-arrow{border-color:var(--nav-text-hover-color) transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent var(--nav-menu-background-color) transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:var(--nav-menu-background-color);-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent var(--nav-menu-foreground-color);border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:var(--nav-menu-foreground-color);background-image:none;border:0 !important;color:var(--nav-menu-foreground-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent var(--nav-text-hover-color)}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:var(--nav-menu-background-color);height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent var(--nav-menu-foreground-color) transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:var(--nav-menu-foreground-color) transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:var(--nav-gradient-image)}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:var(--nav-menu-background-color)}} \ No newline at end of file +.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0px/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.sm-dox{background-image:url("tab_b.png")}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0px 12px;padding-right:43px;font-family:"Lucida Grande","Geneva","Helvetica",Arial,sans-serif;font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:0px 1px 1px rgba(255,255,255,0.9);color:#283A5D;outline:none}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:#fff;text-shadow:0px 1px 1px #000}.sm-dox a.current{color:#D23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:rgba(255,255,255,0.5);border-radius:5px}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{border-radius:0}.sm-dox ul{background:rgba(162,162,162,0.1)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:white;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:#fff;text-shadow:0px 1px 1px #000}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media (min-width: 768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:url("tab_b.png");line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:#283A5D transparent transparent transparent;background:transparent;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0px 12px;background-image:url("tab_s.png");background-repeat:no-repeat;background-position:right;border-radius:0 !important}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:#fff;text-shadow:0px 1px 1px #000}.sm-dox a:hover span.sub-arrow{border-color:#fff transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent #fff transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:#fff;border-radius:5px !important;box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent #555;border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:#555;background-image:none;border:0 !important;color:#555;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:#fff;text-shadow:0px 1px 1px #000}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent #fff}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:#fff;height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #D23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#D23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent #555 transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:#555 transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:url("tab_b.png")}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:#fff}}