ruby_odeum 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +504 -0
- data/LICENSE +504 -0
- data/README +50 -0
- data/bin/odeum_mgr +106 -0
- data/doc/rdoc/classes/Odeum.html +235 -0
- data/doc/rdoc/classes/Odeum.src/M000010.html +25 -0
- data/doc/rdoc/classes/Odeum.src/M000011.html +22 -0
- data/doc/rdoc/classes/Odeum.src/M000012.html +27 -0
- data/doc/rdoc/classes/Odeum.src/M000013.html +27 -0
- data/doc/rdoc/classes/Odeum.src/M000014.html +28 -0
- data/doc/rdoc/classes/Odeum/Document.html +382 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000040.html +25 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000041.html +22 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000042.html +23 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000043.html +23 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000044.html +24 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000045.html +32 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000046.html +22 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000047.html +22 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000048.html +22 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000049.html +22 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000050.html +24 -0
- data/doc/rdoc/classes/Odeum/Document.src/M000051.html +27 -0
- data/doc/rdoc/classes/Odeum/Index.html +662 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000015.html +46 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000016.html +33 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000017.html +35 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000018.html +23 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000019.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000020.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000021.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000022.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000023.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000024.html +29 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000025.html +23 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000026.html +24 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000027.html +23 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000028.html +26 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000029.html +24 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000030.html +20 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000031.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000032.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000033.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000034.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000035.html +20 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000036.html +20 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000037.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000038.html +22 -0
- data/doc/rdoc/classes/Odeum/Index.src/M000039.html +22 -0
- data/doc/rdoc/classes/OdeumTest.html +257 -0
- data/doc/rdoc/classes/OdeumTest.src/M000001.html +18 -0
- data/doc/rdoc/classes/OdeumTest.src/M000002.html +19 -0
- data/doc/rdoc/classes/OdeumTest.src/M000003.html +27 -0
- data/doc/rdoc/classes/OdeumTest.src/M000004.html +25 -0
- data/doc/rdoc/classes/OdeumTest.src/M000005.html +44 -0
- data/doc/rdoc/classes/OdeumTest.src/M000006.html +20 -0
- data/doc/rdoc/classes/OdeumTest.src/M000007.html +39 -0
- data/doc/rdoc/classes/OdeumTest.src/M000008.html +59 -0
- data/doc/rdoc/classes/OdeumTest.src/M000009.html +41 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/COPYING.html +756 -0
- data/doc/rdoc/files/LICENSE.html +756 -0
- data/doc/rdoc/files/README.html +175 -0
- data/doc/rdoc/files/ext/odeum_index/odeum_index_c.html +101 -0
- data/doc/rdoc/files/test/test_odeum_rb.html +109 -0
- data/doc/rdoc/fr_class_index.html +30 -0
- data/doc/rdoc/fr_file_index.html +31 -0
- data/doc/rdoc/fr_method_index.html +77 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/ext/odeum_index/cabin.c +2735 -0
- data/ext/odeum_index/cabin.h +1040 -0
- data/ext/odeum_index/curia.c +1114 -0
- data/ext/odeum_index/curia.h +430 -0
- data/ext/odeum_index/depot.c +1910 -0
- data/ext/odeum_index/depot.h +439 -0
- data/ext/odeum_index/extconf.rb +10 -0
- data/ext/odeum_index/myconf.c +668 -0
- data/ext/odeum_index/myconf.h +523 -0
- data/ext/odeum_index/odeum.c +1743 -0
- data/ext/odeum_index/odeum.h +541 -0
- data/ext/odeum_index/odeum_index.c +991 -0
- data/ext/odeum_index/villa.c +1923 -0
- data/ext/odeum_index/villa.h +470 -0
- data/ext/odeum_index/vista.c +159 -0
- data/ext/odeum_index/vista.h +111 -0
- data/test/test_odeum.rb +174 -0
- metadata +138 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
+
<!DOCTYPE html
|
4
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
+
|
7
|
+
<!--
|
8
|
+
|
9
|
+
Methods
|
10
|
+
|
11
|
+
-->
|
12
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
+
<head>
|
14
|
+
<title>Methods</title>
|
15
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
+
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
+
<base target="docwin" />
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<div id="index">
|
21
|
+
<h1 class="section-bar">Methods</h1>
|
22
|
+
<div id="index-entries">
|
23
|
+
<a href="classes/Odeum/Document.html#M000042">[] (Odeum::Document)</a><br />
|
24
|
+
<a href="classes/Odeum/Document.html#M000041">[]= (Odeum::Document)</a><br />
|
25
|
+
<a href="classes/Odeum/Document.html#M000045">add_content (Odeum::Document)</a><br />
|
26
|
+
<a href="classes/Odeum/Document.html#M000044">add_word_list (Odeum::Document)</a><br />
|
27
|
+
<a href="classes/Odeum/Document.html#M000043">addword (Odeum::Document)</a><br />
|
28
|
+
<a href="classes/Odeum/Document.html#M000049">asis_words (Odeum::Document)</a><br />
|
29
|
+
<a href="classes/Odeum.html#M000012">breaktext (Odeum)</a><br />
|
30
|
+
<a href="classes/Odeum/Index.html#M000032">bucket_count (Odeum::Index)</a><br />
|
31
|
+
<a href="classes/Odeum/Index.html#M000033">buckets_used (Odeum::Index)</a><br />
|
32
|
+
<a href="classes/Odeum/Index.html#M000023">check (Odeum::Index)</a><br />
|
33
|
+
<a href="classes/Odeum/Index.html#M000016">close (Odeum::Index)</a><br />
|
34
|
+
<a href="classes/Odeum/Document.html#M000051">close (Odeum::Document)</a><br />
|
35
|
+
<a href="classes/Odeum/Index.html#M000018">delete (Odeum::Index)</a><br />
|
36
|
+
<a href="classes/Odeum/Index.html#M000019">delete_by_id (Odeum::Index)</a><br />
|
37
|
+
<a href="classes/Odeum/Index.html#M000034">doc_count (Odeum::Index)</a><br />
|
38
|
+
<a href="classes/Odeum/Index.html#M000037">fatal_error (Odeum::Index)</a><br />
|
39
|
+
<a href="classes/Odeum/Index.html#M000020">get (Odeum::Index)</a><br />
|
40
|
+
<a href="classes/Odeum/Index.html#M000021">get_by_id (Odeum::Index)</a><br />
|
41
|
+
<a href="classes/Odeum/Index.html#M000022">get_id_by_uri (Odeum::Index)</a><br />
|
42
|
+
<a href="classes/Odeum/Document.html#M000046">id (Odeum::Document)</a><br />
|
43
|
+
<a href="classes/Odeum/Index.html#M000038">inode (Odeum::Index)</a><br />
|
44
|
+
<a href="classes/Odeum/Index.html#M000026">iterator (Odeum::Index)</a><br />
|
45
|
+
<a href="classes/Odeum.html#M000010">merge (Odeum)</a><br />
|
46
|
+
<a href="classes/Odeum/Index.html#M000039">mtime (Odeum::Index)</a><br />
|
47
|
+
<a href="classes/Odeum/Index.html#M000030">name (Odeum::Index)</a><br />
|
48
|
+
<a href="classes/Odeum/Index.html#M000015">new (Odeum::Index)</a><br />
|
49
|
+
<a href="classes/Odeum/Document.html#M000040">new (Odeum::Document)</a><br />
|
50
|
+
<a href="classes/Odeum/Index.html#M000027">next (Odeum::Index)</a><br />
|
51
|
+
<a href="classes/Odeum/Document.html#M000048">normal_words (Odeum::Document)</a><br />
|
52
|
+
<a href="classes/Odeum.html#M000013">normalizeword (Odeum)</a><br />
|
53
|
+
<a href="classes/Odeum/Index.html#M000029">optimize (Odeum::Index)</a><br />
|
54
|
+
<a href="classes/Odeum/Index.html#M000017">put (Odeum::Index)</a><br />
|
55
|
+
<a href="classes/Odeum.html#M000011">remove (Odeum)</a><br />
|
56
|
+
<a href="classes/Odeum/Document.html#M000050">scores (Odeum::Document)</a><br />
|
57
|
+
<a href="classes/Odeum/Index.html#M000024">search (Odeum::Index)</a><br />
|
58
|
+
<a href="classes/Odeum/Index.html#M000025">search_doc_count (Odeum::Index)</a><br />
|
59
|
+
<a href="classes/Odeum.html#M000014">settuning (Odeum)</a><br />
|
60
|
+
<a href="classes/OdeumTest.html#M000001">setup (OdeumTest)</a><br />
|
61
|
+
<a href="classes/Odeum/Index.html#M000031">size (Odeum::Index)</a><br />
|
62
|
+
<a href="classes/Odeum/Index.html#M000028">sync (Odeum::Index)</a><br />
|
63
|
+
<a href="classes/OdeumTest.html#M000002">teardown (OdeumTest)</a><br />
|
64
|
+
<a href="classes/OdeumTest.html#M000003">test_attributes (OdeumTest)</a><br />
|
65
|
+
<a href="classes/OdeumTest.html#M000004">test_document_attr (OdeumTest)</a><br />
|
66
|
+
<a href="classes/OdeumTest.html#M000006">test_document_misc (OdeumTest)</a><br />
|
67
|
+
<a href="classes/OdeumTest.html#M000005">test_document_words (OdeumTest)</a><br />
|
68
|
+
<a href="classes/OdeumTest.html#M000007">test_load_documents (OdeumTest)</a><br />
|
69
|
+
<a href="classes/OdeumTest.html#M000008">test_retrieval_delete (OdeumTest)</a><br />
|
70
|
+
<a href="classes/OdeumTest.html#M000009">test_search (OdeumTest)</a><br />
|
71
|
+
<a href="classes/Odeum/Document.html#M000047">uri (Odeum::Document)</a><br />
|
72
|
+
<a href="classes/Odeum/Index.html#M000035">word_count (Odeum::Index)</a><br />
|
73
|
+
<a href="classes/Odeum/Index.html#M000036">writable (Odeum::Index)</a><br />
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
</body>
|
77
|
+
</html>
|
data/doc/rdoc/index.html
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
+
<!DOCTYPE html
|
3
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
4
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
5
|
+
|
6
|
+
<!--
|
7
|
+
|
8
|
+
RDoc Documentation
|
9
|
+
|
10
|
+
-->
|
11
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
12
|
+
<head>
|
13
|
+
<title>RDoc Documentation</title>
|
14
|
+
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
15
|
+
</head>
|
16
|
+
<frameset rows="20%, 80%">
|
17
|
+
<frameset cols="25%,35%,45%">
|
18
|
+
<frame src="fr_file_index.html" title="Files" name="Files" />
|
19
|
+
<frame src="fr_class_index.html" name="Classes" />
|
20
|
+
<frame src="fr_method_index.html" name="Methods" />
|
21
|
+
</frameset>
|
22
|
+
<frame src="files/README.html" name="docwin" />
|
23
|
+
</frameset>
|
24
|
+
</html>
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
body {
|
3
|
+
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
+
font-size: 90%;
|
5
|
+
margin: 0;
|
6
|
+
margin-left: 40px;
|
7
|
+
padding: 0;
|
8
|
+
background: white;
|
9
|
+
}
|
10
|
+
|
11
|
+
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
+
h1 { font-size: 150%; }
|
13
|
+
h2,h3,h4 { margin-top: 1em; }
|
14
|
+
|
15
|
+
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
+
a:hover { background: #039; color: #eef; }
|
17
|
+
|
18
|
+
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
+
td > a {
|
20
|
+
background: transparent;
|
21
|
+
color: #039;
|
22
|
+
text-decoration: none;
|
23
|
+
}
|
24
|
+
|
25
|
+
/* and inside a section title */
|
26
|
+
.section-title > a {
|
27
|
+
background: transparent;
|
28
|
+
color: #eee;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
/* === Structural elements =================================== */
|
33
|
+
|
34
|
+
div#index {
|
35
|
+
margin: 0;
|
36
|
+
margin-left: -40px;
|
37
|
+
padding: 0;
|
38
|
+
font-size: 90%;
|
39
|
+
}
|
40
|
+
|
41
|
+
|
42
|
+
div#index a {
|
43
|
+
margin-left: 0.7em;
|
44
|
+
}
|
45
|
+
|
46
|
+
div#index .section-bar {
|
47
|
+
margin-left: 0px;
|
48
|
+
padding-left: 0.7em;
|
49
|
+
background: #ccc;
|
50
|
+
font-size: small;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
div#classHeader, div#fileHeader {
|
55
|
+
width: auto;
|
56
|
+
color: white;
|
57
|
+
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
+
margin: 0;
|
59
|
+
margin-left: -40px;
|
60
|
+
border-bottom: 3px solid #006;
|
61
|
+
}
|
62
|
+
|
63
|
+
div#classHeader a, div#fileHeader a {
|
64
|
+
background: inherit;
|
65
|
+
color: white;
|
66
|
+
}
|
67
|
+
|
68
|
+
div#classHeader td, div#fileHeader td {
|
69
|
+
background: inherit;
|
70
|
+
color: white;
|
71
|
+
}
|
72
|
+
|
73
|
+
|
74
|
+
div#fileHeader {
|
75
|
+
background: #057;
|
76
|
+
}
|
77
|
+
|
78
|
+
div#classHeader {
|
79
|
+
background: #048;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.class-name-in-header {
|
84
|
+
font-size: 180%;
|
85
|
+
font-weight: bold;
|
86
|
+
}
|
87
|
+
|
88
|
+
|
89
|
+
div#bodyContent {
|
90
|
+
padding: 0 1.5em 0 1.5em;
|
91
|
+
}
|
92
|
+
|
93
|
+
div#description {
|
94
|
+
padding: 0.5em 1.5em;
|
95
|
+
background: #efefef;
|
96
|
+
border: 1px dotted #999;
|
97
|
+
}
|
98
|
+
|
99
|
+
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
+
color: #125;;
|
101
|
+
background: transparent;
|
102
|
+
}
|
103
|
+
|
104
|
+
div#validator-badges {
|
105
|
+
text-align: center;
|
106
|
+
}
|
107
|
+
div#validator-badges img { border: 0; }
|
108
|
+
|
109
|
+
div#copyright {
|
110
|
+
color: #333;
|
111
|
+
background: #efefef;
|
112
|
+
font: 0.75em sans-serif;
|
113
|
+
margin-top: 5em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
padding: 0.5em 2em;
|
116
|
+
}
|
117
|
+
|
118
|
+
|
119
|
+
/* === Classes =================================== */
|
120
|
+
|
121
|
+
table.header-table {
|
122
|
+
color: white;
|
123
|
+
font-size: small;
|
124
|
+
}
|
125
|
+
|
126
|
+
.type-note {
|
127
|
+
font-size: small;
|
128
|
+
color: #DEDEDE;
|
129
|
+
}
|
130
|
+
|
131
|
+
.xxsection-bar {
|
132
|
+
background: #eee;
|
133
|
+
color: #333;
|
134
|
+
padding: 3px;
|
135
|
+
}
|
136
|
+
|
137
|
+
.section-bar {
|
138
|
+
color: #333;
|
139
|
+
border-bottom: 1px solid #999;
|
140
|
+
margin-left: -20px;
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
.section-title {
|
145
|
+
background: #79a;
|
146
|
+
color: #eee;
|
147
|
+
padding: 3px;
|
148
|
+
margin-top: 2em;
|
149
|
+
margin-left: -30px;
|
150
|
+
border: 1px solid #999;
|
151
|
+
}
|
152
|
+
|
153
|
+
.top-aligned-row { vertical-align: top }
|
154
|
+
.bottom-aligned-row { vertical-align: bottom }
|
155
|
+
|
156
|
+
/* --- Context section classes ----------------------- */
|
157
|
+
|
158
|
+
.context-row { }
|
159
|
+
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
+
.context-item-value { font-size: small; color: #448; }
|
161
|
+
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
+
|
163
|
+
/* --- Method classes -------------------------- */
|
164
|
+
.method-detail {
|
165
|
+
background: #efefef;
|
166
|
+
padding: 0;
|
167
|
+
margin-top: 0.5em;
|
168
|
+
margin-bottom: 1em;
|
169
|
+
border: 1px dotted #ccc;
|
170
|
+
}
|
171
|
+
.method-heading {
|
172
|
+
color: black;
|
173
|
+
background: #ccc;
|
174
|
+
border-bottom: 1px solid #666;
|
175
|
+
padding: 0.2em 0.5em 0 0.5em;
|
176
|
+
}
|
177
|
+
.method-signature { color: black; background: inherit; }
|
178
|
+
.method-name { font-weight: bold; }
|
179
|
+
.method-args { font-style: italic; }
|
180
|
+
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
+
|
182
|
+
/* --- Source code sections -------------------- */
|
183
|
+
|
184
|
+
a.source-toggle { font-size: 90%; }
|
185
|
+
div.method-source-code {
|
186
|
+
background: #262626;
|
187
|
+
color: #ffdead;
|
188
|
+
margin: 1em;
|
189
|
+
padding: 0.5em;
|
190
|
+
border: 1px dashed #999;
|
191
|
+
overflow: hidden;
|
192
|
+
}
|
193
|
+
|
194
|
+
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
+
|
196
|
+
/* --- Ruby keyword styles --------------------- */
|
197
|
+
|
198
|
+
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
+
|
200
|
+
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
+
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
+
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
+
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
+
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
+
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
+
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
+
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
+
.ruby-value { color: #7fffd4; background: transparent; }
|
@@ -0,0 +1,2735 @@
|
|
1
|
+
/*************************************************************************************************
|
2
|
+
* Implementation of Cabin
|
3
|
+
* Copyright (C) 2000-2005 Mikio Hirabayashi
|
4
|
+
* This file is part of QDBM, Quick Database Manager.
|
5
|
+
* QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
|
6
|
+
* Lesser General Public License as published by the Free Software Foundation; either version
|
7
|
+
* 2.1 of the License or any later version. QDBM is distributed in the hope that it will be
|
8
|
+
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
9
|
+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
10
|
+
* details.
|
11
|
+
* You should have received a copy of the GNU Lesser General Public License along with QDBM; if
|
12
|
+
* not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
13
|
+
* 02111-1307 USA.
|
14
|
+
*************************************************************************************************/
|
15
|
+
|
16
|
+
|
17
|
+
#include "cabin.h"
|
18
|
+
#include "myconf.h"
|
19
|
+
|
20
|
+
#define CB_GCUNIT 64 /* allocation unit size of a buffer in gc */
|
21
|
+
#define CB_SPBUFSIZ 32 /* size of a buffer for sprintf */
|
22
|
+
#define CB_SPMAXWIDTH 128 /* max width of a column for sprintf */
|
23
|
+
#define CB_DATUMUNIT 16 /* allocation unit size of a datum handle */
|
24
|
+
#define CB_LISTUNIT 64 /* allocation unit number of a list handle */
|
25
|
+
#define CB_MAPBNUM 4093 /* bucket size of a map handle */
|
26
|
+
#define CB_MAPPBNUM 251 /* bucket size of a petit map handle */
|
27
|
+
#define CB_MAPCSUNIT 64 /* small allocation unit size of map concatenation */
|
28
|
+
#define CB_MAPCBUNIT 256 /* big allocation unit size of map concatenation */
|
29
|
+
#define CB_MSGBUFSIZ 256 /* size of a buffer for log message */
|
30
|
+
#define CB_IOBUFSIZ 4096 /* size of an I/O buffer */
|
31
|
+
#define CB_FILEMODE 00644 /* permission of a creating file */
|
32
|
+
#define CB_ENCBUFSIZ 32 /* size of a buffer for encoding name */
|
33
|
+
#define CB_DATEBUFSIZ 64 /* size of a buffer for date expression */
|
34
|
+
#define CB_VNUMBUFSIZ 8 /* size of a buffer for variable length number */
|
35
|
+
|
36
|
+
|
37
|
+
/* private function prototypes */
|
38
|
+
static void cbmyfatal(const char *message);
|
39
|
+
static void cbggchandler(void);
|
40
|
+
static void cbggckeeper(void *ptr, void (*func)(void *));
|
41
|
+
static void cbqsortsub(char *bp, int nmemb, int size, char *pswap, char *vswap,
|
42
|
+
int(*compar)(const void *, const void *));
|
43
|
+
static int cblistelemcmp(const void *a, const void *b);
|
44
|
+
static int cbfirsthash(const char *kbuf, int ksiz);
|
45
|
+
static int cbsecondhash(const char *kbuf, int ksiz);
|
46
|
+
static int cbkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
|
47
|
+
static int cbsetvnumbuf(char *buf, int num);
|
48
|
+
static int cbreadvnumbuf(const char *buf, int size, int *sp);
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
/*************************************************************************************************
|
53
|
+
* public objects
|
54
|
+
*************************************************************************************************/
|
55
|
+
|
56
|
+
|
57
|
+
/* Call back function for handling a fatal error. */
|
58
|
+
void (*cbfatalfunc)(const char *message) = NULL;
|
59
|
+
|
60
|
+
|
61
|
+
/* Allocate a region on memory. */
|
62
|
+
void *cbmalloc(size_t size){
|
63
|
+
char *p;
|
64
|
+
assert(size > 0 && size < INT_MAX);
|
65
|
+
if(!(p = malloc(size))){
|
66
|
+
if(cbfatalfunc){
|
67
|
+
cbfatalfunc("out of memory");
|
68
|
+
} else {
|
69
|
+
cbmyfatal("out of memory");
|
70
|
+
}
|
71
|
+
}
|
72
|
+
return p;
|
73
|
+
}
|
74
|
+
|
75
|
+
|
76
|
+
/* Re-allocate a region on memory. */
|
77
|
+
void *cbrealloc(void *ptr, size_t size){
|
78
|
+
char *p;
|
79
|
+
assert(size > 0);
|
80
|
+
if(!(p = realloc(ptr, size))){
|
81
|
+
if(cbfatalfunc){
|
82
|
+
cbfatalfunc("out of memory");
|
83
|
+
} else {
|
84
|
+
cbmyfatal("out of memory");
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return p;
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
/* Duplicate a region on memory. */
|
92
|
+
char *cbmemdup(const char *ptr, int size){
|
93
|
+
char *p;
|
94
|
+
assert(ptr);
|
95
|
+
if(size < 0) size = strlen(ptr);
|
96
|
+
p = cbmalloc(size + 1);
|
97
|
+
memcpy(p, ptr, size);
|
98
|
+
p[size] = '\0';
|
99
|
+
return p;
|
100
|
+
}
|
101
|
+
|
102
|
+
|
103
|
+
/* Register the pointer or handle of an object to the global garbage collector. */
|
104
|
+
void cbglobalgc(void *ptr, void (*func)(void *)){
|
105
|
+
assert(ptr && func);
|
106
|
+
cbggckeeper(ptr, func);
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
/* Exercise the global garbage collector explicitly. */
|
111
|
+
void cbggcsweep(void){
|
112
|
+
cbggckeeper(NULL, NULL);
|
113
|
+
}
|
114
|
+
|
115
|
+
|
116
|
+
/* Sort an array using insert sort. */
|
117
|
+
void cbisort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
|
118
|
+
char *bp, *swap;
|
119
|
+
int i, j;
|
120
|
+
assert(base && nmemb >= 0 && size > 0 && compar);
|
121
|
+
bp = (char *)base;
|
122
|
+
swap = cbmalloc(size);
|
123
|
+
for(i = 1; i < nmemb; i++){
|
124
|
+
if(compar(bp + (i - 1) * size, bp + i * size) > 0){
|
125
|
+
memcpy(swap, bp + i * size, size);
|
126
|
+
for(j = i; j > 0; j--){
|
127
|
+
if(compar(bp + (j - 1) * size, swap) < 0) break;
|
128
|
+
memcpy(bp + j * size, bp + (j - 1) * size, size);
|
129
|
+
}
|
130
|
+
memcpy(bp + j * size, swap, size);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
free(swap);
|
134
|
+
}
|
135
|
+
|
136
|
+
|
137
|
+
/* Sort an array using shell sort. */
|
138
|
+
void cbssort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
|
139
|
+
char *bp, *swap;
|
140
|
+
int step, bottom, i, j;
|
141
|
+
assert(base && nmemb >= 0 && size > 0 && compar);
|
142
|
+
bp = (char *)base;
|
143
|
+
swap = cbmalloc(size);
|
144
|
+
for(step = (nmemb - 1) / 3; step >= 0; step = (step - 1) / 3){
|
145
|
+
if(step < 5) step = 1;
|
146
|
+
for(bottom = 0; bottom < step; bottom++){
|
147
|
+
for(i = bottom + step; i < nmemb; i += step){
|
148
|
+
if(compar(bp + (i - step) * size, bp + i * size) > 0){
|
149
|
+
memcpy(swap, bp + i * size, size);
|
150
|
+
for(j = i; j > step - 1; j -= step){
|
151
|
+
if(compar(bp + (j - step) * size, swap) < 0) break;
|
152
|
+
memcpy(bp + j * size, bp + (j - step) * size, size);
|
153
|
+
}
|
154
|
+
memcpy(bp + j * size, swap, size);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
if(step < 2) break;
|
159
|
+
}
|
160
|
+
free(swap);
|
161
|
+
}
|
162
|
+
|
163
|
+
|
164
|
+
/* Sort an array using heap sort. */
|
165
|
+
void cbhsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
|
166
|
+
char *bp, *swap;
|
167
|
+
int top, bottom, mybot, i;
|
168
|
+
assert(base && nmemb >= 0 && size > 0 && compar);
|
169
|
+
bp = (char *)base;
|
170
|
+
nmemb--;
|
171
|
+
bottom = nmemb / 2 +1;
|
172
|
+
top = nmemb;
|
173
|
+
swap = cbmalloc(size);
|
174
|
+
while(bottom > 0){
|
175
|
+
bottom--;
|
176
|
+
mybot = bottom;
|
177
|
+
i = 2 * mybot;
|
178
|
+
while(i <= top) {
|
179
|
+
if(i < top && compar(bp + (i + 1) * size, bp + i * size) > 0) i++;
|
180
|
+
if(compar(bp + mybot * size, bp + i * size) >= 0) break;
|
181
|
+
memcpy(swap, bp + mybot * size, size);
|
182
|
+
memcpy(bp + mybot * size, bp + i * size, size);
|
183
|
+
memcpy(bp + i * size, swap, size);
|
184
|
+
mybot = i;
|
185
|
+
i = 2 * mybot;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
while(top > 0){
|
189
|
+
memcpy(swap, bp, size);
|
190
|
+
memcpy(bp, bp + top * size, size);
|
191
|
+
memcpy(bp + top * size, swap, size);
|
192
|
+
top--;
|
193
|
+
mybot = bottom;
|
194
|
+
i = 2 * mybot;
|
195
|
+
while(i <= top) {
|
196
|
+
if(i < top && compar(bp + (i + 1) * size, bp + i * size) > 0) i++;
|
197
|
+
if(compar(bp + mybot * size, bp + i * size) >= 0) break;
|
198
|
+
memcpy(swap, bp + mybot * size, size);
|
199
|
+
memcpy(bp + mybot * size, bp + i * size, size);
|
200
|
+
memcpy(bp + i * size, swap, size);
|
201
|
+
mybot = i;
|
202
|
+
i = 2 * mybot;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
free(swap);
|
206
|
+
}
|
207
|
+
|
208
|
+
|
209
|
+
/* Sort an array using quick sort. */
|
210
|
+
void cbqsort(void *base, int nmemb, int size, int(*compar)(const void *, const void *)){
|
211
|
+
char *pswap, *vswap;
|
212
|
+
assert(base && nmemb >= 0 && size > 0 && compar);
|
213
|
+
pswap = cbmalloc(size);
|
214
|
+
vswap = cbmalloc(size);
|
215
|
+
cbqsortsub(base, nmemb, size, pswap, vswap, compar);
|
216
|
+
free(vswap);
|
217
|
+
free(pswap);
|
218
|
+
}
|
219
|
+
|
220
|
+
|
221
|
+
/* Compare two strings with case insensitive evaluation. */
|
222
|
+
int cbstricmp(const char *astr, const char *bstr){
|
223
|
+
int ac, bc;
|
224
|
+
assert(astr && bstr);
|
225
|
+
while(*astr != '\0'){
|
226
|
+
if(*bstr == '\0') return 1;
|
227
|
+
ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
|
228
|
+
bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr;
|
229
|
+
if(ac != bc) return ac - bc;
|
230
|
+
astr++;
|
231
|
+
bstr++;
|
232
|
+
}
|
233
|
+
return *bstr == '\0' ? 0 : -1;
|
234
|
+
}
|
235
|
+
|
236
|
+
|
237
|
+
/* Check whether a string begins with a key. */
|
238
|
+
int cbstrfwmatch(const char *str, const char *key){
|
239
|
+
int len, i;
|
240
|
+
assert(str && key);
|
241
|
+
len = strlen(key);
|
242
|
+
for(i = 0; i < len; i++){
|
243
|
+
if(str[i] != key[i] || str[i] == '\0') return FALSE;
|
244
|
+
}
|
245
|
+
return TRUE;
|
246
|
+
}
|
247
|
+
|
248
|
+
|
249
|
+
/* Check whether a string begins with a key, with case insensitive evaluation. */
|
250
|
+
int cbstrfwimatch(const char *str, const char *key){
|
251
|
+
int len, i, sc, kc;
|
252
|
+
assert(str && key);
|
253
|
+
len = strlen(key);
|
254
|
+
for(i = 0; i < len; i++){
|
255
|
+
sc = str[i];
|
256
|
+
if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
|
257
|
+
kc = key[i];
|
258
|
+
if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
|
259
|
+
if(sc != kc || sc == '\0') return FALSE;
|
260
|
+
}
|
261
|
+
return TRUE;
|
262
|
+
}
|
263
|
+
|
264
|
+
|
265
|
+
/* Check whether a string ends with a key. */
|
266
|
+
int cbstrbwmatch(const char *str, const char *key){
|
267
|
+
int slen, klen, i;
|
268
|
+
assert(str && key);
|
269
|
+
slen = strlen(str);
|
270
|
+
klen = strlen(key);
|
271
|
+
for(i = 1; i <= klen; i++){
|
272
|
+
if(str[slen-i] != key[klen-i] || i > slen) return FALSE;
|
273
|
+
}
|
274
|
+
return TRUE;
|
275
|
+
}
|
276
|
+
|
277
|
+
|
278
|
+
/* Check whether a string ends with a key, with case insensitive evaluation. */
|
279
|
+
int cbstrbwimatch(const char *str, const char *key){
|
280
|
+
int slen, klen, i, sc, kc;
|
281
|
+
assert(str && key);
|
282
|
+
slen = strlen(str);
|
283
|
+
klen = strlen(key);
|
284
|
+
for(i = 1; i <= klen; i++){
|
285
|
+
sc = str[slen-i];
|
286
|
+
if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
|
287
|
+
kc = key[klen-i];
|
288
|
+
if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
|
289
|
+
if(sc != kc || i > slen) return FALSE;
|
290
|
+
}
|
291
|
+
return TRUE;
|
292
|
+
}
|
293
|
+
|
294
|
+
|
295
|
+
/* Convert the letters of a string to upper case. */
|
296
|
+
char *cbstrtoupper(char *str){
|
297
|
+
int i;
|
298
|
+
assert(str);
|
299
|
+
for(i = 0; str[i] != '\0'; i++){
|
300
|
+
if(str[i] >= 'a' && str[i] <= 'z') str[i] -= 'a' - 'A';
|
301
|
+
}
|
302
|
+
return str;
|
303
|
+
}
|
304
|
+
|
305
|
+
|
306
|
+
/* Convert the letters of a string to lower case. */
|
307
|
+
char *cbstrtolower(char *str){
|
308
|
+
int i;
|
309
|
+
assert(str);
|
310
|
+
for(i = 0; str[i] != '\0'; i++){
|
311
|
+
if(str[i] >= 'A' && str[i] <= 'Z') str[i] += 'a' - 'A';
|
312
|
+
}
|
313
|
+
return str;
|
314
|
+
}
|
315
|
+
|
316
|
+
|
317
|
+
/* Cut the space characters at head or tail of a string. */
|
318
|
+
char *cbstrtrim(char *str){
|
319
|
+
char *wp;
|
320
|
+
int i, head;
|
321
|
+
assert(str);
|
322
|
+
wp = str;
|
323
|
+
head = TRUE;
|
324
|
+
for(i = 0; str[i] != '\0'; i++){
|
325
|
+
if((str[i] >= 0x07 && str[i] <= 0x0d) || str[i] == 0x20){
|
326
|
+
if(!head) *(wp++) = str[i];
|
327
|
+
} else {
|
328
|
+
*(wp++) = str[i];
|
329
|
+
head = FALSE;
|
330
|
+
}
|
331
|
+
}
|
332
|
+
*wp = '\0';
|
333
|
+
while(wp > str && ((wp[-1] >= 0x07 && wp[-1] <= 0x0d) || wp[-1] == 0x20)){
|
334
|
+
*(--wp) = '\0';
|
335
|
+
}
|
336
|
+
return str;
|
337
|
+
}
|
338
|
+
|
339
|
+
|
340
|
+
/* Get a datum handle. */
|
341
|
+
CBDATUM *cbdatumopen(const char *ptr, int size){
|
342
|
+
CBDATUM *datum;
|
343
|
+
datum = cbmalloc(sizeof(*datum));
|
344
|
+
datum->dptr = cbmalloc(CB_DATUMUNIT);
|
345
|
+
datum->dptr[0] = '\0';
|
346
|
+
datum->dsize = 0;
|
347
|
+
datum->asize = CB_DATUMUNIT;
|
348
|
+
if(ptr) cbdatumcat(datum, ptr, size);
|
349
|
+
return datum;
|
350
|
+
}
|
351
|
+
|
352
|
+
|
353
|
+
/* Copy a datum. */
|
354
|
+
CBDATUM *cbdatumdup(const CBDATUM *datum){
|
355
|
+
assert(datum);
|
356
|
+
return cbdatumopen(datum->dptr, datum->dsize);
|
357
|
+
}
|
358
|
+
|
359
|
+
|
360
|
+
/* Free a datum handle. */
|
361
|
+
void cbdatumclose(CBDATUM *datum){
|
362
|
+
assert(datum);
|
363
|
+
free(datum->dptr);
|
364
|
+
free(datum);
|
365
|
+
}
|
366
|
+
|
367
|
+
|
368
|
+
/* Concatenate a datum and a region. */
|
369
|
+
void cbdatumcat(CBDATUM *datum, const char *ptr, int size){
|
370
|
+
assert(datum && ptr);
|
371
|
+
if(size < 0) size = strlen(ptr);
|
372
|
+
if(datum->dsize + size >= datum->asize){
|
373
|
+
datum->asize = datum->asize * 2 + size + 1;
|
374
|
+
datum->dptr = cbrealloc(datum->dptr, datum->asize);
|
375
|
+
}
|
376
|
+
memmove(datum->dptr + datum->dsize, ptr, size);
|
377
|
+
datum->dsize += size;
|
378
|
+
datum->dptr[datum->dsize] = '\0';
|
379
|
+
}
|
380
|
+
|
381
|
+
|
382
|
+
/* Get the pointer of the region of a datum. */
|
383
|
+
const char *cbdatumptr(const CBDATUM *datum){
|
384
|
+
assert(datum);
|
385
|
+
return datum->dptr;
|
386
|
+
}
|
387
|
+
|
388
|
+
|
389
|
+
/* Get the size of the region of a datum. */
|
390
|
+
int cbdatumsize(const CBDATUM *datum){
|
391
|
+
assert(datum);
|
392
|
+
return datum->dsize;
|
393
|
+
}
|
394
|
+
|
395
|
+
|
396
|
+
/* Set the size of the region of a datum. */
|
397
|
+
void cbdatumsetsize(CBDATUM *datum, int size){
|
398
|
+
assert(datum && size >= 0);
|
399
|
+
if(size <= datum->dsize){
|
400
|
+
datum->dsize = size;
|
401
|
+
datum->dptr[size] = '\0';
|
402
|
+
} else {
|
403
|
+
if(size >= datum->asize){
|
404
|
+
datum->asize = datum->asize * 2 + size + 1;
|
405
|
+
datum->dptr = cbrealloc(datum->dptr, datum->asize);
|
406
|
+
}
|
407
|
+
memset(datum->dptr + datum->dsize, 0, (size - datum->dsize) + 1);
|
408
|
+
datum->dsize = size;
|
409
|
+
}
|
410
|
+
}
|
411
|
+
|
412
|
+
|
413
|
+
/* Convert a datum to an allocated region. */
|
414
|
+
char *cbdatumtomalloc(CBDATUM *datum, int *sp){
|
415
|
+
char *ptr;
|
416
|
+
assert(datum);
|
417
|
+
ptr = datum->dptr;
|
418
|
+
if(sp) *sp = datum->dsize;
|
419
|
+
free(datum);
|
420
|
+
return ptr;
|
421
|
+
}
|
422
|
+
|
423
|
+
|
424
|
+
/* Get a list handle. */
|
425
|
+
CBLIST *cblistopen(void){
|
426
|
+
CBLIST *list;
|
427
|
+
list = cbmalloc(sizeof(*list));
|
428
|
+
list->anum = CB_LISTUNIT;
|
429
|
+
list->array = cbmalloc(sizeof(list->array[0]) * list->anum);
|
430
|
+
list->start = 0;
|
431
|
+
list->num = 0;
|
432
|
+
return list;
|
433
|
+
}
|
434
|
+
|
435
|
+
|
436
|
+
/* Copy a list. */
|
437
|
+
CBLIST *cblistdup(const CBLIST *list){
|
438
|
+
CBLIST *newlist;
|
439
|
+
int i, size;
|
440
|
+
const char *val;
|
441
|
+
assert(list);
|
442
|
+
newlist = cblistopen();
|
443
|
+
for(i = 0; i < cblistnum(list); i++){
|
444
|
+
val = cblistval(list, i, &size);
|
445
|
+
cblistpush(newlist, val, size);
|
446
|
+
}
|
447
|
+
return newlist;
|
448
|
+
}
|
449
|
+
|
450
|
+
|
451
|
+
/* Close a list handle. */
|
452
|
+
void cblistclose(CBLIST *list){
|
453
|
+
int i, end;
|
454
|
+
assert(list);
|
455
|
+
end = list->start + list->num;
|
456
|
+
for(i = list->start; i < end; i++){
|
457
|
+
free(list->array[i].dptr);
|
458
|
+
}
|
459
|
+
free(list->array);
|
460
|
+
free(list);
|
461
|
+
}
|
462
|
+
|
463
|
+
|
464
|
+
/* Get the number of elements of a list. */
|
465
|
+
int cblistnum(const CBLIST *list){
|
466
|
+
assert(list);
|
467
|
+
return list->num;
|
468
|
+
}
|
469
|
+
|
470
|
+
|
471
|
+
/* Get the pointer to the region of an element. */
|
472
|
+
const char *cblistval(const CBLIST *list, int index, int *sp){
|
473
|
+
assert(list && index >= 0);
|
474
|
+
if(index >= list->num) return NULL;
|
475
|
+
index += list->start;
|
476
|
+
if(sp) *sp = list->array[index].dsize;
|
477
|
+
return list->array[index].dptr;
|
478
|
+
}
|
479
|
+
|
480
|
+
|
481
|
+
/* Add an element at the end of a list. */
|
482
|
+
void cblistpush(CBLIST *list, const char *ptr, int size){
|
483
|
+
int index;
|
484
|
+
assert(list && ptr);
|
485
|
+
if(size < 0) size = strlen(ptr);
|
486
|
+
index = list->start + list->num;
|
487
|
+
if(index >= list->anum){
|
488
|
+
list->anum *= 2;
|
489
|
+
list->array = cbrealloc(list->array, list->anum * sizeof(list->array[0]));
|
490
|
+
}
|
491
|
+
list->array[index].dptr = cbmalloc((size < CB_DATUMUNIT ? CB_DATUMUNIT : size) + 1);
|
492
|
+
memcpy(list->array[index].dptr, ptr, size);
|
493
|
+
list->array[index].dptr[size] = '\0';
|
494
|
+
list->array[index].dsize = size;
|
495
|
+
list->num++;
|
496
|
+
}
|
497
|
+
|
498
|
+
|
499
|
+
/* Remove an element of the end of a list. */
|
500
|
+
char *cblistpop(CBLIST *list, int *sp){
|
501
|
+
int index;
|
502
|
+
assert(list);
|
503
|
+
if(list->num < 1) return NULL;
|
504
|
+
index = list->start + list->num - 1;
|
505
|
+
list->num--;
|
506
|
+
if(sp) *sp = list->array[index].dsize;
|
507
|
+
return list->array[index].dptr;
|
508
|
+
}
|
509
|
+
|
510
|
+
|
511
|
+
/* Add an element at the top of a list. */
|
512
|
+
void cblistunshift(CBLIST *list, const char *ptr, int size){
|
513
|
+
int index;
|
514
|
+
assert(list && ptr);
|
515
|
+
if(size < 0) size = strlen(ptr);
|
516
|
+
if(list->start < 1){
|
517
|
+
if(list->start + list->num >= list->anum){
|
518
|
+
list->anum *= 2;
|
519
|
+
list->array = cbrealloc(list->array, list->anum * sizeof(list->array[0]));
|
520
|
+
}
|
521
|
+
list->start = list->anum - list->num;
|
522
|
+
memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
|
523
|
+
}
|
524
|
+
index = list->start - 1;
|
525
|
+
list->array[index].dptr = cbmalloc((size < CB_DATUMUNIT ? CB_DATUMUNIT : size) + 1);
|
526
|
+
memcpy(list->array[index].dptr, ptr, size);
|
527
|
+
list->array[index].dptr[size] = '\0';
|
528
|
+
list->array[index].dsize = size;
|
529
|
+
list->start--;
|
530
|
+
list->num++;
|
531
|
+
}
|
532
|
+
|
533
|
+
|
534
|
+
/* Remove an element of the top of a list. */
|
535
|
+
char *cblistshift(CBLIST *list, int *sp){
|
536
|
+
int index;
|
537
|
+
assert(list);
|
538
|
+
if(list->num < 1) return NULL;
|
539
|
+
index = list->start;
|
540
|
+
list->start++;
|
541
|
+
list->num--;
|
542
|
+
if(sp) *sp = list->array[index].dsize;
|
543
|
+
return list->array[index].dptr;
|
544
|
+
}
|
545
|
+
|
546
|
+
|
547
|
+
/* Add an element at the specified location of a list. */
|
548
|
+
void cblistinsert(CBLIST *list, int index, const char *ptr, int size){
|
549
|
+
assert(list && index >= 0);
|
550
|
+
if(index > list->num) return;
|
551
|
+
if(size < 0) size = strlen(ptr);
|
552
|
+
index += list->start;
|
553
|
+
if(list->start + list->num >= list->anum){
|
554
|
+
list->anum *= 2;
|
555
|
+
list->array = cbrealloc(list->array, list->anum * sizeof(list->array[0]));
|
556
|
+
}
|
557
|
+
memmove(list->array + index + 1, list->array + index,
|
558
|
+
sizeof(list->array[0]) * (list->start + list->num - index));
|
559
|
+
list->array[index].dptr = cbmemdup(ptr, size);
|
560
|
+
list->array[index].dsize = size;
|
561
|
+
list->num++;
|
562
|
+
}
|
563
|
+
|
564
|
+
|
565
|
+
/* Remove an element at the specified location of a list. */
|
566
|
+
char *cblistremove(CBLIST *list, int index, int *sp){
|
567
|
+
char *dptr;
|
568
|
+
assert(list && index >= 0);
|
569
|
+
if(index >= list->num) return NULL;
|
570
|
+
index += list->start;
|
571
|
+
dptr = list->array[index].dptr;
|
572
|
+
if(sp) *sp = list->array[index].dsize;
|
573
|
+
list->num--;
|
574
|
+
memmove(list->array + index, list->array + index + 1,
|
575
|
+
sizeof(list->array[0]) * (list->start + list->num - index));
|
576
|
+
return dptr;
|
577
|
+
}
|
578
|
+
|
579
|
+
|
580
|
+
/* Overwrite an element at the specified location of a list. */
|
581
|
+
void cblistover(CBLIST *list, int index, const char *ptr, int size){
|
582
|
+
assert(list && index >= 0);
|
583
|
+
if(index >= list->num) return;
|
584
|
+
if(size < 0) size = strlen(ptr);
|
585
|
+
index += list->start;
|
586
|
+
if(size > list->array[index].dsize)
|
587
|
+
list->array[index].dptr = cbrealloc(list->array[index].dptr, size + 1);
|
588
|
+
memcpy(list->array[index].dptr, ptr, size);
|
589
|
+
list->array[index].dsize = size;
|
590
|
+
list->array[index].dptr[size] = '\0';
|
591
|
+
}
|
592
|
+
|
593
|
+
|
594
|
+
/* Sort elements of a list in lexical order. */
|
595
|
+
void cblistsort(CBLIST *list){
|
596
|
+
assert(list);
|
597
|
+
cbqsort(list->array + list->start, list->num, sizeof(list->array[0]), cblistelemcmp);
|
598
|
+
}
|
599
|
+
|
600
|
+
|
601
|
+
/* Search a list for an element using liner search. */
|
602
|
+
int cblistlsearch(const CBLIST *list, const char *ptr, int size){
|
603
|
+
int i, end;
|
604
|
+
assert(list && ptr);
|
605
|
+
if(size < 0) size = strlen(ptr);
|
606
|
+
end = list->start + list->num;
|
607
|
+
for(i = list->start; i < end; i++){
|
608
|
+
if(list->array[i].dsize == size && !memcmp(list->array[i].dptr, ptr, size))
|
609
|
+
return i - list->start;
|
610
|
+
}
|
611
|
+
return -1;
|
612
|
+
}
|
613
|
+
|
614
|
+
|
615
|
+
/* Search a list for an element using binary search. */
|
616
|
+
int cblistbsearch(const CBLIST *list, const char *ptr, int size){
|
617
|
+
CBLISTDATUM key, *res;
|
618
|
+
assert(list && ptr);
|
619
|
+
if(size < 0) size = strlen(ptr);
|
620
|
+
key.dptr = cbmemdup(ptr, size);
|
621
|
+
key.dsize = size;
|
622
|
+
res = bsearch(&key, list->array + list->start, list->num, sizeof(list->array[0]), cblistelemcmp);
|
623
|
+
free(key.dptr);
|
624
|
+
return res ? (res - list->array - list->start) : -1;
|
625
|
+
}
|
626
|
+
|
627
|
+
|
628
|
+
/* Serialize a list into a byte array. */
|
629
|
+
char *cblistdump(const CBLIST *list, int *sp){
|
630
|
+
char *buf, vnumbuf[CB_VNUMBUFSIZ];
|
631
|
+
const char *vbuf;
|
632
|
+
int i, bsiz, vnumsiz, ln, vsiz;
|
633
|
+
assert(list && sp);
|
634
|
+
ln = cblistnum(list);
|
635
|
+
vnumsiz = cbsetvnumbuf(vnumbuf, ln);
|
636
|
+
buf = cbmalloc(vnumsiz + 1);
|
637
|
+
memcpy(buf, vnumbuf, vnumsiz);
|
638
|
+
bsiz = vnumsiz;
|
639
|
+
for(i = 0; i < ln; i++){
|
640
|
+
vbuf = cblistval(list, i, &vsiz);
|
641
|
+
vnumsiz = cbsetvnumbuf(vnumbuf, vsiz);
|
642
|
+
buf = cbrealloc(buf, bsiz + vnumsiz + vsiz + 1);
|
643
|
+
memcpy(buf + bsiz, vnumbuf, vnumsiz);
|
644
|
+
bsiz += vnumsiz;
|
645
|
+
memcpy(buf + bsiz, vbuf, vsiz);
|
646
|
+
bsiz += vsiz;
|
647
|
+
}
|
648
|
+
*sp = bsiz;
|
649
|
+
return buf;
|
650
|
+
}
|
651
|
+
|
652
|
+
|
653
|
+
/* Redintegrate a serialized list. */
|
654
|
+
CBLIST *cblistload(const char *ptr, int size){
|
655
|
+
CBLIST *list;
|
656
|
+
const char *rp;
|
657
|
+
int i, step, ln, vsiz;
|
658
|
+
assert(ptr && size >= 0);
|
659
|
+
list = cblistopen();
|
660
|
+
rp = ptr;
|
661
|
+
ln = cbreadvnumbuf(rp, size, &step);
|
662
|
+
rp += step;
|
663
|
+
size -= step;
|
664
|
+
if(ln > size) return list;
|
665
|
+
for(i = 0; i < ln; i++){
|
666
|
+
if(size < 1) break;
|
667
|
+
vsiz = cbreadvnumbuf(rp, size, &step);
|
668
|
+
rp += step;
|
669
|
+
size -= step;
|
670
|
+
if(vsiz > size) break;
|
671
|
+
cblistpush(list, rp, vsiz);
|
672
|
+
rp += vsiz;
|
673
|
+
}
|
674
|
+
return list;
|
675
|
+
}
|
676
|
+
|
677
|
+
|
678
|
+
/* Get a map handle. */
|
679
|
+
CBMAP *cbmapopen(void){
|
680
|
+
return cbmapopenex(CB_MAPBNUM);
|
681
|
+
}
|
682
|
+
|
683
|
+
|
684
|
+
/* Copy a map. */
|
685
|
+
CBMAP *cbmapdup(CBMAP *map){
|
686
|
+
CBMAP *newmap;
|
687
|
+
const char *kbuf, *vbuf;
|
688
|
+
int ksiz, vsiz;
|
689
|
+
assert(map);
|
690
|
+
cbmapiterinit(map);
|
691
|
+
newmap = map->rnum > CB_MAPPBNUM ? cbmapopen() : cbmapopenex(CB_MAPPBNUM);
|
692
|
+
while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
|
693
|
+
vbuf = cbmapget(map, kbuf, ksiz, &vsiz);
|
694
|
+
cbmapput(newmap, kbuf, ksiz, vbuf, vsiz, FALSE);
|
695
|
+
}
|
696
|
+
cbmapiterinit(map);
|
697
|
+
return newmap;
|
698
|
+
}
|
699
|
+
|
700
|
+
|
701
|
+
/* Close a map handle. */
|
702
|
+
void cbmapclose(CBMAP *map){
|
703
|
+
CBMAPDATUM *datum, *next;
|
704
|
+
datum = map->first;
|
705
|
+
while(datum){
|
706
|
+
next = (CBMAPDATUM *)(datum->next);
|
707
|
+
free(datum->kbuf);
|
708
|
+
free(datum->vbuf);
|
709
|
+
free(datum);
|
710
|
+
datum = next;
|
711
|
+
}
|
712
|
+
free(map->buckets);
|
713
|
+
free(map);
|
714
|
+
}
|
715
|
+
|
716
|
+
|
717
|
+
/* Store a record. */
|
718
|
+
int cbmapput(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int over){
|
719
|
+
CBMAPDATUM *datum, **entp;
|
720
|
+
int bidx, hash, kcmp;
|
721
|
+
assert(map && kbuf && vbuf);
|
722
|
+
if(ksiz < 0) ksiz = strlen(kbuf);
|
723
|
+
if(vsiz < 0) vsiz = strlen(vbuf);
|
724
|
+
bidx = cbfirsthash(kbuf, ksiz) % map->bnum;
|
725
|
+
datum = map->buckets[bidx];
|
726
|
+
entp = map->buckets + bidx;
|
727
|
+
hash = cbsecondhash(kbuf, ksiz);
|
728
|
+
while(datum){
|
729
|
+
if(hash > datum->hash){
|
730
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
731
|
+
datum = (CBMAPDATUM *)datum->left;
|
732
|
+
} else if(hash < datum->hash){
|
733
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
734
|
+
datum = (CBMAPDATUM *)datum->right;
|
735
|
+
} else {
|
736
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
737
|
+
if(kcmp < 0){
|
738
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
739
|
+
datum = (CBMAPDATUM *)datum->left;
|
740
|
+
} else if(kcmp > 0){
|
741
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
742
|
+
datum = (CBMAPDATUM *)datum->right;
|
743
|
+
} else {
|
744
|
+
if(!over) return FALSE;
|
745
|
+
if(vsiz > datum->vsiz){
|
746
|
+
free(datum->vbuf);
|
747
|
+
datum->vbuf = cbmemdup(vbuf, vsiz);
|
748
|
+
} else {
|
749
|
+
memcpy(datum->vbuf, vbuf, vsiz);
|
750
|
+
datum->vbuf[vsiz] = '\0';
|
751
|
+
}
|
752
|
+
datum->vsiz = vsiz;
|
753
|
+
return TRUE;
|
754
|
+
}
|
755
|
+
}
|
756
|
+
}
|
757
|
+
datum = cbmalloc(sizeof(*datum));
|
758
|
+
datum->kbuf = cbmemdup(kbuf, ksiz);
|
759
|
+
datum->ksiz = ksiz;
|
760
|
+
datum->vbuf = cbmemdup(vbuf, vsiz);
|
761
|
+
datum->vsiz = vsiz;
|
762
|
+
datum->hash = hash;
|
763
|
+
datum->left = NULL;
|
764
|
+
datum->right = NULL;
|
765
|
+
datum->prev = (char *)map->last;
|
766
|
+
datum->next = NULL;
|
767
|
+
*entp = datum;
|
768
|
+
if(!map->first) map->first = datum;
|
769
|
+
if(map->last) map->last->next = (char *)datum;
|
770
|
+
map->last = datum;
|
771
|
+
map->rnum++;
|
772
|
+
return TRUE;
|
773
|
+
}
|
774
|
+
|
775
|
+
|
776
|
+
/* Concatenate a value at the end of the value of the existing record. */
|
777
|
+
void cbmapputcat(CBMAP *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
|
778
|
+
CBMAPDATUM *datum, **entp;
|
779
|
+
int bidx, hash, kcmp, asiz, unit;
|
780
|
+
assert(map && kbuf && vbuf);
|
781
|
+
if(ksiz < 0) ksiz = strlen(kbuf);
|
782
|
+
if(vsiz < 0) vsiz = strlen(vbuf);
|
783
|
+
bidx = cbfirsthash(kbuf, ksiz) % map->bnum;
|
784
|
+
datum = map->buckets[bidx];
|
785
|
+
entp = map->buckets + bidx;
|
786
|
+
hash = cbsecondhash(kbuf, ksiz);
|
787
|
+
while(datum){
|
788
|
+
if(hash > datum->hash){
|
789
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
790
|
+
datum = (CBMAPDATUM *)datum->left;
|
791
|
+
} else if(hash < datum->hash){
|
792
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
793
|
+
datum = (CBMAPDATUM *)datum->right;
|
794
|
+
} else {
|
795
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
796
|
+
if(kcmp < 0){
|
797
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
798
|
+
datum = (CBMAPDATUM *)datum->left;
|
799
|
+
} else if(kcmp > 0){
|
800
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
801
|
+
datum = (CBMAPDATUM *)datum->right;
|
802
|
+
} else {
|
803
|
+
asiz = datum->vsiz + vsiz;
|
804
|
+
unit = asiz <= CB_MAPCSUNIT ? CB_MAPCSUNIT : CB_MAPCBUNIT;
|
805
|
+
asiz = (asiz - 1) + unit - (asiz - 1) % unit;
|
806
|
+
datum->vbuf = cbrealloc(datum->vbuf, asiz + 1);
|
807
|
+
memcpy(datum->vbuf + datum->vsiz, vbuf, vsiz);
|
808
|
+
*(datum->vbuf + datum->vsiz + vsiz) = '\0';
|
809
|
+
datum->vsiz += vsiz;
|
810
|
+
return;
|
811
|
+
}
|
812
|
+
}
|
813
|
+
}
|
814
|
+
unit = vsiz <= CB_MAPCSUNIT ? CB_MAPCSUNIT : CB_MAPCBUNIT;
|
815
|
+
asiz = (vsiz - 1) + unit - (vsiz - 1) % unit;
|
816
|
+
datum = cbmalloc(sizeof(*datum));
|
817
|
+
datum->kbuf = cbmemdup(kbuf, ksiz);
|
818
|
+
datum->ksiz = ksiz;
|
819
|
+
datum->vbuf = cbmalloc(asiz + 1);
|
820
|
+
memcpy(datum->vbuf, vbuf, vsiz);
|
821
|
+
*(datum->vbuf + vsiz) = '\0';
|
822
|
+
datum->vsiz = vsiz;
|
823
|
+
datum->hash = hash;
|
824
|
+
datum->left = NULL;
|
825
|
+
datum->right = NULL;
|
826
|
+
datum->prev = (char *)map->last;
|
827
|
+
datum->next = NULL;
|
828
|
+
*entp = datum;
|
829
|
+
if(!map->first) map->first = datum;
|
830
|
+
if(map->last) map->last->next = (char *)datum;
|
831
|
+
map->last = datum;
|
832
|
+
map->rnum++;
|
833
|
+
}
|
834
|
+
|
835
|
+
|
836
|
+
/* Delete a record. */
|
837
|
+
int cbmapout(CBMAP *map, const char *kbuf, int ksiz){
|
838
|
+
CBMAPDATUM *datum, **entp, *tmp;
|
839
|
+
int bidx, hash, kcmp;
|
840
|
+
assert(map && kbuf);
|
841
|
+
if(ksiz < 0) ksiz = strlen(kbuf);
|
842
|
+
bidx = cbfirsthash(kbuf, ksiz) % map->bnum;
|
843
|
+
datum = map->buckets[bidx];
|
844
|
+
entp = map->buckets + bidx;
|
845
|
+
hash = cbsecondhash(kbuf, ksiz);
|
846
|
+
while(datum){
|
847
|
+
if(hash > datum->hash){
|
848
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
849
|
+
datum = (CBMAPDATUM *)datum->left;
|
850
|
+
} else if(hash < datum->hash){
|
851
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
852
|
+
datum = (CBMAPDATUM *)datum->right;
|
853
|
+
} else {
|
854
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
855
|
+
if(kcmp < 0){
|
856
|
+
entp = (CBMAPDATUM **)&(datum->left);
|
857
|
+
datum = (CBMAPDATUM *)datum->left;
|
858
|
+
} else if(kcmp > 0){
|
859
|
+
entp = (CBMAPDATUM **)&(datum->right);
|
860
|
+
datum = (CBMAPDATUM *)datum->right;
|
861
|
+
} else {
|
862
|
+
if(datum->prev) ((CBMAPDATUM *)(datum->prev))->next = datum->next;
|
863
|
+
if(datum->next) ((CBMAPDATUM *)(datum->next))->prev = datum->prev;
|
864
|
+
if(datum == map->first) map->first = (CBMAPDATUM *)datum->next;
|
865
|
+
if(datum == map->last) map->last = (CBMAPDATUM *)datum->prev;
|
866
|
+
if(datum->left && !datum->right){
|
867
|
+
*entp = (CBMAPDATUM *)datum->left;
|
868
|
+
} else if(!datum->left && datum->right){
|
869
|
+
*entp = (CBMAPDATUM *)datum->right;
|
870
|
+
} else if(!datum->left && !datum->left){
|
871
|
+
*entp = NULL;
|
872
|
+
} else {
|
873
|
+
*entp = (CBMAPDATUM *)datum->left;
|
874
|
+
tmp = *entp;
|
875
|
+
while(TRUE){
|
876
|
+
if(hash > tmp->hash){
|
877
|
+
if(tmp->left){
|
878
|
+
tmp = (CBMAPDATUM *)tmp->left;
|
879
|
+
} else {
|
880
|
+
tmp->left = datum->right;
|
881
|
+
break;
|
882
|
+
}
|
883
|
+
} else if(hash < tmp->hash){
|
884
|
+
if(tmp->right){
|
885
|
+
tmp = (CBMAPDATUM *)tmp->right;
|
886
|
+
} else {
|
887
|
+
tmp->right = datum->right;
|
888
|
+
break;
|
889
|
+
}
|
890
|
+
} else {
|
891
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
892
|
+
if(kcmp < 0){
|
893
|
+
if(tmp->left){
|
894
|
+
tmp = (CBMAPDATUM *)tmp->left;
|
895
|
+
} else {
|
896
|
+
tmp->left = datum->right;
|
897
|
+
break;
|
898
|
+
}
|
899
|
+
} else {
|
900
|
+
if(tmp->right){
|
901
|
+
tmp = (CBMAPDATUM *)tmp->right;
|
902
|
+
} else {
|
903
|
+
tmp->right = datum->right;
|
904
|
+
break;
|
905
|
+
}
|
906
|
+
}
|
907
|
+
}
|
908
|
+
}
|
909
|
+
}
|
910
|
+
free(datum->kbuf);
|
911
|
+
free(datum->vbuf);
|
912
|
+
free(datum);
|
913
|
+
map->rnum--;
|
914
|
+
return TRUE;
|
915
|
+
}
|
916
|
+
}
|
917
|
+
}
|
918
|
+
return FALSE;
|
919
|
+
}
|
920
|
+
|
921
|
+
|
922
|
+
/* Retrieve a record. */
|
923
|
+
const char *cbmapget(const CBMAP *map, const char *kbuf, int ksiz, int *sp){
|
924
|
+
CBMAPDATUM *datum;
|
925
|
+
int hash, kcmp;
|
926
|
+
assert(map && kbuf);
|
927
|
+
if(ksiz < 0) ksiz = strlen(kbuf);
|
928
|
+
datum = map->buckets[cbfirsthash(kbuf, ksiz)%map->bnum];
|
929
|
+
hash = cbsecondhash(kbuf, ksiz);
|
930
|
+
while(datum){
|
931
|
+
if(hash > datum->hash){
|
932
|
+
datum = (CBMAPDATUM *)datum->left;
|
933
|
+
} else if(hash < datum->hash){
|
934
|
+
datum = (CBMAPDATUM *)datum->right;
|
935
|
+
} else {
|
936
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
937
|
+
if(kcmp < 0){
|
938
|
+
datum = (CBMAPDATUM *)datum->left;
|
939
|
+
} else if(kcmp > 0){
|
940
|
+
datum = (CBMAPDATUM *)datum->right;
|
941
|
+
} else {
|
942
|
+
if(sp) *sp = datum->vsiz;
|
943
|
+
return datum->vbuf;
|
944
|
+
}
|
945
|
+
}
|
946
|
+
}
|
947
|
+
return NULL;
|
948
|
+
}
|
949
|
+
|
950
|
+
|
951
|
+
/* Move a record to the edge. */
|
952
|
+
int cbmapmove(CBMAP *map, const char *kbuf, int ksiz, int head){
|
953
|
+
CBMAPDATUM *datum;
|
954
|
+
int hash, kcmp;
|
955
|
+
assert(map && kbuf);
|
956
|
+
if(ksiz < 0) ksiz = strlen(kbuf);
|
957
|
+
datum = map->buckets[cbfirsthash(kbuf, ksiz)%map->bnum];
|
958
|
+
hash = cbsecondhash(kbuf, ksiz);
|
959
|
+
while(datum){
|
960
|
+
if(hash > datum->hash){
|
961
|
+
datum = (CBMAPDATUM *)datum->left;
|
962
|
+
} else if(hash < datum->hash){
|
963
|
+
datum = (CBMAPDATUM *)datum->right;
|
964
|
+
} else {
|
965
|
+
kcmp = cbkeycmp(kbuf, ksiz, datum->kbuf, datum->ksiz);
|
966
|
+
if(kcmp < 0){
|
967
|
+
datum = (CBMAPDATUM *)datum->left;
|
968
|
+
} else if(kcmp > 0){
|
969
|
+
datum = (CBMAPDATUM *)datum->right;
|
970
|
+
} else {
|
971
|
+
if(head){
|
972
|
+
if(map->first == datum) return TRUE;
|
973
|
+
if(map->last == datum) map->last = (CBMAPDATUM *)(datum->prev);
|
974
|
+
if(datum->prev) ((CBMAPDATUM *)(datum->prev))->next = datum->next;
|
975
|
+
if(datum->next) ((CBMAPDATUM *)(datum->next))->prev = datum->prev;
|
976
|
+
datum->prev = NULL;
|
977
|
+
datum->next = (char *)(map->first);
|
978
|
+
map->first->prev = (char *)datum;
|
979
|
+
map->first = datum;
|
980
|
+
} else {
|
981
|
+
if(map->last == datum) return TRUE;
|
982
|
+
if(map->first == datum) map->first = (CBMAPDATUM *)(datum->next);
|
983
|
+
if(datum->prev) ((CBMAPDATUM *)(datum->prev))->next = datum->next;
|
984
|
+
if(datum->next) ((CBMAPDATUM *)(datum->next))->prev = datum->prev;
|
985
|
+
datum->prev = (char *)(map->last);
|
986
|
+
datum->next = NULL;
|
987
|
+
map->last->next = (char *)datum;
|
988
|
+
map->last = datum;
|
989
|
+
}
|
990
|
+
return TRUE;
|
991
|
+
}
|
992
|
+
}
|
993
|
+
}
|
994
|
+
return FALSE;
|
995
|
+
}
|
996
|
+
|
997
|
+
|
998
|
+
/* Initialize the iterator of a map handle. */
|
999
|
+
void cbmapiterinit(CBMAP *map){
|
1000
|
+
assert(map);
|
1001
|
+
map->cur = map->first;
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
|
1005
|
+
/* Get the next key of the iterator. */
|
1006
|
+
const char *cbmapiternext(CBMAP *map, int *sp){
|
1007
|
+
CBMAPDATUM *datum;
|
1008
|
+
assert(map);
|
1009
|
+
if(!map->cur) return NULL;
|
1010
|
+
datum = map->cur;
|
1011
|
+
map->cur = (CBMAPDATUM *)datum->next;
|
1012
|
+
if(sp) *sp = datum->ksiz;
|
1013
|
+
return datum->kbuf;
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
|
1017
|
+
/* Get the number of the records stored in a map. */
|
1018
|
+
int cbmaprnum(const CBMAP *map){
|
1019
|
+
assert(map);
|
1020
|
+
return map->rnum;
|
1021
|
+
}
|
1022
|
+
|
1023
|
+
|
1024
|
+
/* Get the list handle contains all keys in a map. */
|
1025
|
+
CBLIST *cbmapkeys(CBMAP *map){
|
1026
|
+
CBLIST *list;
|
1027
|
+
const char *kbuf;
|
1028
|
+
int ksiz;
|
1029
|
+
assert(map);
|
1030
|
+
list = cblistopen();
|
1031
|
+
cbmapiterinit(map);
|
1032
|
+
while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
|
1033
|
+
cblistpush(list, kbuf, ksiz);
|
1034
|
+
}
|
1035
|
+
return list;
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
|
1039
|
+
/* Get the list handle contains all values in a map. */
|
1040
|
+
CBLIST *cbmapvals(CBMAP *map){
|
1041
|
+
CBLIST *list;
|
1042
|
+
const char *kbuf, *vbuf;
|
1043
|
+
int ksiz, vsiz;
|
1044
|
+
assert(map);
|
1045
|
+
list = cblistopen();
|
1046
|
+
cbmapiterinit(map);
|
1047
|
+
while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
|
1048
|
+
vbuf = cbmapget(map, kbuf, ksiz, &vsiz);
|
1049
|
+
cblistpush(list, vbuf, vsiz);
|
1050
|
+
}
|
1051
|
+
return list;
|
1052
|
+
}
|
1053
|
+
|
1054
|
+
|
1055
|
+
/* Serialize a map into a byte array. */
|
1056
|
+
char *cbmapdump(CBMAP *map, int *sp){
|
1057
|
+
char *buf, vnumbuf[CB_VNUMBUFSIZ];
|
1058
|
+
const char *kbuf, *vbuf;
|
1059
|
+
int bsiz, vnumsiz, rn, ksiz, vsiz;
|
1060
|
+
assert(map && sp);
|
1061
|
+
rn = cbmaprnum(map);
|
1062
|
+
vnumsiz = cbsetvnumbuf(vnumbuf, rn);
|
1063
|
+
buf = cbmalloc(vnumsiz + 1);
|
1064
|
+
memcpy(buf, vnumbuf, vnumsiz);
|
1065
|
+
bsiz = vnumsiz;
|
1066
|
+
cbmapiterinit(map);
|
1067
|
+
while((kbuf = cbmapiternext(map, &ksiz)) != NULL){
|
1068
|
+
vbuf = cbmapget(map, kbuf, ksiz, &vsiz);
|
1069
|
+
vnumsiz = cbsetvnumbuf(vnumbuf, ksiz);
|
1070
|
+
buf = cbrealloc(buf, bsiz + vnumsiz + ksiz + 1);
|
1071
|
+
memcpy(buf + bsiz, vnumbuf, vnumsiz);
|
1072
|
+
bsiz += vnumsiz;
|
1073
|
+
memcpy(buf + bsiz, kbuf, ksiz);
|
1074
|
+
bsiz += ksiz;
|
1075
|
+
vnumsiz = cbsetvnumbuf(vnumbuf, vsiz);
|
1076
|
+
buf = cbrealloc(buf, bsiz + vnumsiz + vsiz + 1);
|
1077
|
+
memcpy(buf + bsiz, vnumbuf, vnumsiz);
|
1078
|
+
bsiz += vnumsiz;
|
1079
|
+
memcpy(buf + bsiz, vbuf, vsiz);
|
1080
|
+
bsiz += vsiz;
|
1081
|
+
}
|
1082
|
+
*sp = bsiz;
|
1083
|
+
return buf;
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
|
1087
|
+
/* Redintegrate a serialized map. */
|
1088
|
+
CBMAP *cbmapload(const char *ptr, int size){
|
1089
|
+
CBMAP *map;
|
1090
|
+
const char *rp, *kbuf, *vbuf;
|
1091
|
+
int i, step, rn, ksiz, vsiz;
|
1092
|
+
assert(ptr && size >= 0);
|
1093
|
+
map = cbmapopenex(CB_MAPPBNUM);
|
1094
|
+
rp = ptr;
|
1095
|
+
rn = cbreadvnumbuf(rp, size, &step);
|
1096
|
+
rp += step;
|
1097
|
+
size -= step;
|
1098
|
+
if(rn > size) return map;
|
1099
|
+
for(i = 0; i < rn; i++){
|
1100
|
+
if(size < 1) break;
|
1101
|
+
ksiz = cbreadvnumbuf(rp, size, &step);
|
1102
|
+
rp += step;
|
1103
|
+
size -= step;
|
1104
|
+
if(ksiz > size) break;
|
1105
|
+
kbuf = rp;
|
1106
|
+
rp += ksiz;
|
1107
|
+
if(size < 1) break;
|
1108
|
+
vsiz = cbreadvnumbuf(rp, size, &step);
|
1109
|
+
rp += step;
|
1110
|
+
size -= step;
|
1111
|
+
if(vsiz > size) break;
|
1112
|
+
vbuf = rp;
|
1113
|
+
rp += vsiz;
|
1114
|
+
cbmapput(map, kbuf, ksiz, vbuf, vsiz, TRUE);
|
1115
|
+
}
|
1116
|
+
return map;
|
1117
|
+
}
|
1118
|
+
|
1119
|
+
|
1120
|
+
/* Allocate a formatted string on memory. */
|
1121
|
+
char *cbsprintf(const char *format, ...){
|
1122
|
+
va_list ap;
|
1123
|
+
char *buf, cbuf[CB_SPBUFSIZ], *str;
|
1124
|
+
int len, cblen, num, slen;
|
1125
|
+
unsigned int unum;
|
1126
|
+
double dnum;
|
1127
|
+
va_start(ap, format);
|
1128
|
+
assert(format);
|
1129
|
+
buf = cbmalloc(1);
|
1130
|
+
len = 0;
|
1131
|
+
while(*format != '\0'){
|
1132
|
+
if(*format == '%'){
|
1133
|
+
cbuf[0] = '%';
|
1134
|
+
cblen = 1;
|
1135
|
+
format++;
|
1136
|
+
while(strchr("0123456789 .+-", *format) && *format != '\0' && cblen < CB_SPBUFSIZ - 1){
|
1137
|
+
cbuf[cblen++] = *format;
|
1138
|
+
format++;
|
1139
|
+
}
|
1140
|
+
cbuf[cblen] = '\0';
|
1141
|
+
if(atoi(cbuf + 1) > CB_SPMAXWIDTH - 16){
|
1142
|
+
sprintf(cbuf, "(err)");
|
1143
|
+
} else {
|
1144
|
+
cbuf[cblen++] = *format;
|
1145
|
+
cbuf[cblen] = '\0';
|
1146
|
+
}
|
1147
|
+
switch(*format){
|
1148
|
+
case 'd':
|
1149
|
+
num = va_arg(ap, int);
|
1150
|
+
buf = cbrealloc(buf, len + CB_SPMAXWIDTH + 2);
|
1151
|
+
len += sprintf(buf + len, cbuf, num);
|
1152
|
+
break;
|
1153
|
+
case 'o': case 'u': case 'x': case 'X': case 'c':
|
1154
|
+
unum = va_arg(ap, unsigned int);
|
1155
|
+
buf = cbrealloc(buf, len + CB_SPMAXWIDTH + 2);
|
1156
|
+
len += sprintf(buf + len, cbuf, unum);
|
1157
|
+
break;
|
1158
|
+
case 'e': case 'E': case 'f': case 'g': case 'G':
|
1159
|
+
dnum = va_arg(ap, double);
|
1160
|
+
buf = cbrealloc(buf, len + CB_SPMAXWIDTH + 2);
|
1161
|
+
len += sprintf(buf + len, cbuf, dnum);
|
1162
|
+
break;
|
1163
|
+
case 's':
|
1164
|
+
str = va_arg(ap, char *);
|
1165
|
+
slen = strlen(str);
|
1166
|
+
buf = cbrealloc(buf, len + slen + 2);
|
1167
|
+
memcpy(buf + len, str, slen);
|
1168
|
+
len += slen;
|
1169
|
+
break;
|
1170
|
+
case '%':
|
1171
|
+
buf = cbrealloc(buf, len + 2);
|
1172
|
+
buf[len++] = '%';
|
1173
|
+
break;
|
1174
|
+
default:
|
1175
|
+
break;
|
1176
|
+
}
|
1177
|
+
} else {
|
1178
|
+
buf = cbrealloc(buf, len + 2);
|
1179
|
+
buf[len++] = *format;
|
1180
|
+
}
|
1181
|
+
format++;
|
1182
|
+
}
|
1183
|
+
buf[len] = '\0';
|
1184
|
+
va_end(ap);
|
1185
|
+
return buf;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
|
1189
|
+
/* Replace some patterns in a string. */
|
1190
|
+
char *cbreplace(const char *str, CBMAP *pairs){
|
1191
|
+
int i, bsiz, wi, rep, ksiz, vsiz;
|
1192
|
+
char *buf;
|
1193
|
+
const char *key, *val;
|
1194
|
+
assert(str && pairs);
|
1195
|
+
bsiz = CB_DATUMUNIT;
|
1196
|
+
buf = cbmalloc(bsiz);
|
1197
|
+
wi = 0;
|
1198
|
+
while(*str != '\0'){
|
1199
|
+
rep = FALSE;
|
1200
|
+
cbmapiterinit(pairs);
|
1201
|
+
while((key = cbmapiternext(pairs, &ksiz)) != NULL){
|
1202
|
+
for(i = 0; i < ksiz; i++){
|
1203
|
+
if(str[i] == '\0' || str[i] != key[i]) break;
|
1204
|
+
}
|
1205
|
+
if(i == ksiz){
|
1206
|
+
val = cbmapget(pairs, key, ksiz, &vsiz);
|
1207
|
+
if(wi + vsiz >= bsiz){
|
1208
|
+
bsiz = bsiz * 2 + vsiz;
|
1209
|
+
buf = cbrealloc(buf, bsiz);
|
1210
|
+
}
|
1211
|
+
memcpy(buf + wi, val, vsiz);
|
1212
|
+
wi += vsiz;
|
1213
|
+
str += ksiz;
|
1214
|
+
rep = TRUE;
|
1215
|
+
break;
|
1216
|
+
}
|
1217
|
+
}
|
1218
|
+
if(!rep){
|
1219
|
+
if(wi + 1 >= bsiz){
|
1220
|
+
bsiz = bsiz * 2 + 1;
|
1221
|
+
buf = cbrealloc(buf, bsiz);
|
1222
|
+
}
|
1223
|
+
buf[wi++] = *str;
|
1224
|
+
str++;
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
buf = cbrealloc(buf, wi + 1);
|
1228
|
+
buf[wi] = '\0';
|
1229
|
+
return buf;
|
1230
|
+
}
|
1231
|
+
|
1232
|
+
|
1233
|
+
/* Make a list by split a serial datum. */
|
1234
|
+
CBLIST *cbsplit(const char *ptr, int size, const char *delim){
|
1235
|
+
CBLIST *list;
|
1236
|
+
int bi, step;
|
1237
|
+
assert(ptr);
|
1238
|
+
list = cblistopen();
|
1239
|
+
if(size < 0) size = strlen(ptr);
|
1240
|
+
if(delim){
|
1241
|
+
for(bi = 0; bi < size; bi += step){
|
1242
|
+
step = 0;
|
1243
|
+
while(bi + step < size && !strchr(delim, ptr[bi+step])){
|
1244
|
+
step++;
|
1245
|
+
}
|
1246
|
+
cblistpush(list, ptr + bi, step);
|
1247
|
+
step++;
|
1248
|
+
}
|
1249
|
+
if(size > 0 && strchr(delim, ptr[size-1])) cblistpush(list, "", 0);
|
1250
|
+
} else {
|
1251
|
+
for(bi = 0; bi < size; bi += step){
|
1252
|
+
step = 0;
|
1253
|
+
while(bi + step < size && ptr[bi+step]){
|
1254
|
+
step++;
|
1255
|
+
}
|
1256
|
+
cblistpush(list, ptr + bi, step);
|
1257
|
+
step++;
|
1258
|
+
}
|
1259
|
+
if(size > 0 && ptr[size-1] == 0) cblistpush(list, "", 0);
|
1260
|
+
}
|
1261
|
+
return list;
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
|
1265
|
+
/* Read whole data of a file. */
|
1266
|
+
char *cbreadfile(const char *name, int *sp){
|
1267
|
+
int fd, size, rv;
|
1268
|
+
char iobuf[CB_IOBUFSIZ], *buf;
|
1269
|
+
if(name){
|
1270
|
+
if((fd = open(name, O_RDONLY, 0)) == -1) return NULL;
|
1271
|
+
} else {
|
1272
|
+
fd = 0;
|
1273
|
+
}
|
1274
|
+
buf = cbmalloc(1);
|
1275
|
+
size = 0;
|
1276
|
+
while((rv = read(fd, iobuf, CB_IOBUFSIZ)) > 0){
|
1277
|
+
buf = cbrealloc(buf, size + rv + 1);
|
1278
|
+
memcpy(buf + size, iobuf, rv);
|
1279
|
+
size += rv;
|
1280
|
+
}
|
1281
|
+
buf[size] = '\0';
|
1282
|
+
if(close(fd) == -1 || rv == -1){
|
1283
|
+
free(buf);
|
1284
|
+
return NULL;
|
1285
|
+
}
|
1286
|
+
if(sp) *sp = size;
|
1287
|
+
return buf;
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
|
1291
|
+
/* Write data of a region into a file. */
|
1292
|
+
int cbwritefile(const char *name, const char *ptr, int size){
|
1293
|
+
int fd, err, wb;
|
1294
|
+
assert(ptr);
|
1295
|
+
if(size < 0) size = strlen(ptr);
|
1296
|
+
if(name){
|
1297
|
+
if((fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, CB_FILEMODE)) == -1) return FALSE;
|
1298
|
+
} else {
|
1299
|
+
fd = 1;
|
1300
|
+
}
|
1301
|
+
err = FALSE;
|
1302
|
+
wb = 0;
|
1303
|
+
do {
|
1304
|
+
wb = write(fd, ptr, size);
|
1305
|
+
switch(wb){
|
1306
|
+
case -1: err = errno != EINTR ? TRUE : FALSE; break;
|
1307
|
+
case 0: break;
|
1308
|
+
default:
|
1309
|
+
ptr += wb;
|
1310
|
+
size -= wb;
|
1311
|
+
break;
|
1312
|
+
}
|
1313
|
+
} while(size > 0);
|
1314
|
+
if(close(fd) == -1) err = TRUE;
|
1315
|
+
return err ? FALSE : TRUE;
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
|
1319
|
+
/* Read every line of a file. */
|
1320
|
+
CBLIST *cbreadlines(const char *name){
|
1321
|
+
char *buf, *tmp;
|
1322
|
+
int vsiz;
|
1323
|
+
CBMAP *pairs;
|
1324
|
+
CBLIST *list;
|
1325
|
+
if(!(buf = cbreadfile(name, NULL))) return NULL;
|
1326
|
+
pairs = cbmapopenex(3);
|
1327
|
+
cbmapput(pairs, "\r\n", 2, "\n", 1, TRUE);
|
1328
|
+
cbmapput(pairs, "\r", 1, "\n", 1, TRUE);
|
1329
|
+
tmp = cbreplace(buf, pairs);
|
1330
|
+
list = cbsplit(tmp, strlen(tmp), "\n");
|
1331
|
+
free(tmp);
|
1332
|
+
cbmapclose(pairs);
|
1333
|
+
free(buf);
|
1334
|
+
if(cblistnum(list) > 0){
|
1335
|
+
cblistval(list, cblistnum(list) - 1, &vsiz);
|
1336
|
+
if(vsiz < 1) free(cblistpop(list, NULL));
|
1337
|
+
}
|
1338
|
+
return list;
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
|
1342
|
+
/* Read names of files in a directory. */
|
1343
|
+
CBLIST *cbdirlist(const char *name){
|
1344
|
+
DIR *DD;
|
1345
|
+
struct dirent *dp;
|
1346
|
+
CBLIST *list;
|
1347
|
+
assert(name);
|
1348
|
+
if(!(DD = opendir(name))) return NULL;
|
1349
|
+
list = cblistopen();
|
1350
|
+
while((dp = readdir(DD)) != NULL){
|
1351
|
+
cblistpush(list, dp->d_name, -1);
|
1352
|
+
}
|
1353
|
+
if(closedir(DD) == -1){
|
1354
|
+
cblistclose(list);
|
1355
|
+
return NULL;
|
1356
|
+
}
|
1357
|
+
return list;
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
|
1361
|
+
/* Get the status of a file or a directory. */
|
1362
|
+
int cbfilestat(const char *name, int *isdirp, int *sizep, int *mtimep){
|
1363
|
+
struct stat sbuf;
|
1364
|
+
assert(name);
|
1365
|
+
if(stat(name, &sbuf) == -1) return FALSE;
|
1366
|
+
if(isdirp) *isdirp = S_ISDIR(sbuf.st_mode);
|
1367
|
+
if(sizep) *sizep = (int)sbuf.st_size;
|
1368
|
+
if(mtimep) *mtimep = (int)sbuf.st_mtime;
|
1369
|
+
return TRUE;
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
|
1373
|
+
/* Break up a URL into elements. */
|
1374
|
+
CBMAP *cburlbreak(const char *str){
|
1375
|
+
CBMAP *map;
|
1376
|
+
char *tmp, *ep;
|
1377
|
+
const char *rp;
|
1378
|
+
int i, serv;
|
1379
|
+
assert(str);
|
1380
|
+
map = cbmapopenex(CB_MAPPBNUM);
|
1381
|
+
rp = str;
|
1382
|
+
while(strchr(" \t\r\n\v\f", *rp)){
|
1383
|
+
rp++;
|
1384
|
+
}
|
1385
|
+
tmp = cbmemdup(rp, -1);
|
1386
|
+
for(i = 0; tmp[i] != '\0'; i++){
|
1387
|
+
if(strchr(" \t\r\n\v\f", tmp[i])){
|
1388
|
+
tmp[i] = '\0';
|
1389
|
+
break;
|
1390
|
+
}
|
1391
|
+
}
|
1392
|
+
rp = tmp;
|
1393
|
+
cbmapput(map, "self", -1, rp, -1, TRUE);
|
1394
|
+
serv = FALSE;
|
1395
|
+
if(cbstrfwimatch(rp, "http://")){
|
1396
|
+
cbmapput(map, "scheme", -1, "http", -1, TRUE);
|
1397
|
+
rp += 7;
|
1398
|
+
serv = TRUE;
|
1399
|
+
} else if(cbstrfwimatch(rp, "https://")){
|
1400
|
+
cbmapput(map, "scheme", -1, "https", -1, TRUE);
|
1401
|
+
rp += 8;
|
1402
|
+
serv = TRUE;
|
1403
|
+
} else if(cbstrfwimatch(rp, "ftp://")){
|
1404
|
+
cbmapput(map, "scheme", -1, "ftp", -1, TRUE);
|
1405
|
+
rp += 6;
|
1406
|
+
serv = TRUE;
|
1407
|
+
} else if(cbstrfwimatch(rp, "file://")){
|
1408
|
+
cbmapput(map, "scheme", -1, "file", -1, TRUE);
|
1409
|
+
rp += 7;
|
1410
|
+
}
|
1411
|
+
if((ep = strchr(rp, '#')) != NULL){
|
1412
|
+
cbmapput(map, "fragment", -1, ep + 1, -1, TRUE);
|
1413
|
+
*ep = '\0';
|
1414
|
+
}
|
1415
|
+
if((ep = strchr(rp, '?')) != NULL){
|
1416
|
+
cbmapput(map, "query", -1, ep + 1, -1, TRUE);
|
1417
|
+
*ep = '\0';
|
1418
|
+
}
|
1419
|
+
if(serv){
|
1420
|
+
if((ep = strchr(rp, '/')) != NULL){
|
1421
|
+
cbmapput(map, "path", -1, ep, -1, TRUE);
|
1422
|
+
*ep = '\0';
|
1423
|
+
} else {
|
1424
|
+
cbmapput(map, "path", -1, "/", -1, TRUE);
|
1425
|
+
}
|
1426
|
+
if((ep = strchr(rp, '@')) != NULL){
|
1427
|
+
*ep = '\0';
|
1428
|
+
if(rp[0] != '\0') cbmapput(map, "authority", -1, rp, -1, TRUE);
|
1429
|
+
rp = ep + 1;
|
1430
|
+
}
|
1431
|
+
if((ep = strchr(rp, ':')) != NULL){
|
1432
|
+
if(ep[1] != '\0') cbmapput(map, "port", -1, ep + 1, -1, TRUE);
|
1433
|
+
*ep = '\0';
|
1434
|
+
}
|
1435
|
+
if(rp[0] != '\0') cbmapput(map, "host", -1, rp, -1, TRUE);
|
1436
|
+
} else {
|
1437
|
+
cbmapput(map, "path", -1, rp, -1, TRUE);
|
1438
|
+
}
|
1439
|
+
free(tmp);
|
1440
|
+
if((rp = cbmapget(map, "path", -1, NULL)) != NULL){
|
1441
|
+
if((ep = strrchr(rp, '/')) != NULL){
|
1442
|
+
if(ep[1] != '\0') cbmapput(map, "file", -1, ep + 1, -1, TRUE);
|
1443
|
+
} else {
|
1444
|
+
cbmapput(map, "file", -1, rp, -1, TRUE);
|
1445
|
+
}
|
1446
|
+
}
|
1447
|
+
if((rp = cbmapget(map, "file", -1, NULL)) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
|
1448
|
+
cbmapout(map, "file", -1);
|
1449
|
+
return map;
|
1450
|
+
}
|
1451
|
+
|
1452
|
+
|
1453
|
+
/* Encode a serial object with URL encoding. */
|
1454
|
+
char *cburlencode(const char *ptr, int size){
|
1455
|
+
char *buf, *wp;
|
1456
|
+
int i, c;
|
1457
|
+
assert(ptr);
|
1458
|
+
if(size < 0) size = strlen(ptr);
|
1459
|
+
buf = cbmalloc(size * 3 + 1);
|
1460
|
+
wp = buf;
|
1461
|
+
for(i = 0; i < size; i++){
|
1462
|
+
c = ((unsigned char *)ptr)[i];
|
1463
|
+
if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
1464
|
+
(c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
|
1465
|
+
*(wp++) = c;
|
1466
|
+
} else {
|
1467
|
+
wp += sprintf(wp, "%%%02X", c);
|
1468
|
+
}
|
1469
|
+
}
|
1470
|
+
*wp = '\0';
|
1471
|
+
return buf;
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
|
1475
|
+
/* Decode a string encoded with URL encoding. */
|
1476
|
+
char *cburldecode(const char *str, int *sp){
|
1477
|
+
const char *hex = "1234567890abcdefABCDEF";
|
1478
|
+
char *buf, *wp;
|
1479
|
+
unsigned char c;
|
1480
|
+
buf = cbmemdup(str, -1);
|
1481
|
+
wp = buf;
|
1482
|
+
while(*str != '\0'){
|
1483
|
+
if(*str == '%'){
|
1484
|
+
str++;
|
1485
|
+
if(strchr(hex, *str) && strchr(hex, *(str + 1))){
|
1486
|
+
c = *str;
|
1487
|
+
if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
|
1488
|
+
if(c >= 'a' && c <= 'z'){
|
1489
|
+
*wp = c - 'a' + 10;
|
1490
|
+
} else {
|
1491
|
+
*wp = c - '0';
|
1492
|
+
}
|
1493
|
+
*wp *= 0x10;
|
1494
|
+
str++;
|
1495
|
+
c = *str;
|
1496
|
+
if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
|
1497
|
+
if(c >= 'a' && c <= 'z'){
|
1498
|
+
*wp += c - 'a' + 10;
|
1499
|
+
} else {
|
1500
|
+
*wp += c - '0';
|
1501
|
+
}
|
1502
|
+
str++;
|
1503
|
+
wp++;
|
1504
|
+
} else {
|
1505
|
+
break;
|
1506
|
+
}
|
1507
|
+
} else if(*str == '+'){
|
1508
|
+
*wp = ' ';
|
1509
|
+
str++;
|
1510
|
+
wp++;
|
1511
|
+
} else {
|
1512
|
+
*wp = *str;
|
1513
|
+
str++;
|
1514
|
+
wp++;
|
1515
|
+
}
|
1516
|
+
}
|
1517
|
+
*wp = '\0';
|
1518
|
+
if(sp) *sp = wp - buf;
|
1519
|
+
return buf;
|
1520
|
+
}
|
1521
|
+
|
1522
|
+
|
1523
|
+
/* Encode a serial object with Base64 encoding. */
|
1524
|
+
char *cbbaseencode(const char *ptr, int size){
|
1525
|
+
char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
1526
|
+
char *buf, *wp;
|
1527
|
+
const unsigned char *obj;
|
1528
|
+
int i;
|
1529
|
+
assert(ptr);
|
1530
|
+
if(size < 0) size = strlen(ptr);
|
1531
|
+
buf = cbmalloc(4 * (size + 2) / 3 + 1);
|
1532
|
+
obj = (const unsigned char *)ptr;
|
1533
|
+
wp = buf;
|
1534
|
+
for(i = 0; i < size; i += 3){
|
1535
|
+
switch(size - i){
|
1536
|
+
case 1:
|
1537
|
+
*wp++ = tbl[obj[0] >> 2];
|
1538
|
+
*wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
|
1539
|
+
*wp++ = '=';
|
1540
|
+
*wp++ = '=';
|
1541
|
+
break;
|
1542
|
+
case 2:
|
1543
|
+
*wp++ = tbl[obj[0] >> 2];
|
1544
|
+
*wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
|
1545
|
+
*wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
|
1546
|
+
*wp++ = '=';
|
1547
|
+
break;
|
1548
|
+
default:
|
1549
|
+
*wp++ = tbl[obj[0] >> 2];
|
1550
|
+
*wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
|
1551
|
+
*wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
|
1552
|
+
*wp++ = tbl[obj[2] & 0x3f];
|
1553
|
+
break;
|
1554
|
+
}
|
1555
|
+
obj += 3;
|
1556
|
+
}
|
1557
|
+
*wp = '\0';
|
1558
|
+
return buf;
|
1559
|
+
}
|
1560
|
+
|
1561
|
+
|
1562
|
+
/* Decode a string encoded with Base64 encoding. */
|
1563
|
+
char *cbbasedecode(const char *str, int *sp){
|
1564
|
+
unsigned char *obj, *wp;
|
1565
|
+
int len, cnt, bpos, i, bits, eqcnt;
|
1566
|
+
assert(str);
|
1567
|
+
cnt = 0;
|
1568
|
+
bpos = 0;
|
1569
|
+
eqcnt = 0;
|
1570
|
+
len = strlen(str);
|
1571
|
+
obj = cbmalloc(len + 1);
|
1572
|
+
wp = obj;
|
1573
|
+
while(bpos < len && eqcnt == 0){
|
1574
|
+
bits = 0;
|
1575
|
+
for(i = 0; bpos < len && i < 4; bpos++){
|
1576
|
+
if(str[bpos] >= 'A' && str[bpos] <= 'Z'){
|
1577
|
+
bits = (bits << 6) | (str[bpos] - 'A');
|
1578
|
+
i++;
|
1579
|
+
} else if(str[bpos] >= 'a' && str[bpos] <= 'z'){
|
1580
|
+
bits = (bits << 6) | (str[bpos] - 'a' + 26);
|
1581
|
+
i++;
|
1582
|
+
} else if(str[bpos] >= '0' && str[bpos] <= '9'){
|
1583
|
+
bits = (bits << 6) | (str[bpos] - '0' + 52);
|
1584
|
+
i++;
|
1585
|
+
} else if(str[bpos] == '+'){
|
1586
|
+
bits = (bits << 6) | 62;
|
1587
|
+
i++;
|
1588
|
+
} else if(str[bpos] == '/'){
|
1589
|
+
bits = (bits << 6) | 63;
|
1590
|
+
i++;
|
1591
|
+
} else if(str[bpos] == '='){
|
1592
|
+
bits <<= 6;
|
1593
|
+
i++;
|
1594
|
+
eqcnt++;
|
1595
|
+
}
|
1596
|
+
}
|
1597
|
+
if(i == 0 && bpos >= len) continue;
|
1598
|
+
switch(eqcnt){
|
1599
|
+
case 0:
|
1600
|
+
*wp++ = (bits >> 16) & 0xff;
|
1601
|
+
*wp++ = (bits >> 8) & 0xff;
|
1602
|
+
*wp++ = bits & 0xff;
|
1603
|
+
cnt += 3;
|
1604
|
+
break;
|
1605
|
+
case 1:
|
1606
|
+
*wp++ = (bits >> 16) & 0xff;
|
1607
|
+
*wp++ = (bits >> 8) & 0xff;
|
1608
|
+
cnt += 2;
|
1609
|
+
break;
|
1610
|
+
case 2:
|
1611
|
+
*wp++ = (bits >> 16) & 0xff;
|
1612
|
+
cnt += 1;
|
1613
|
+
break;
|
1614
|
+
}
|
1615
|
+
}
|
1616
|
+
obj[cnt] = '\0';
|
1617
|
+
if(sp) *sp = cnt;
|
1618
|
+
return (char *)obj;
|
1619
|
+
}
|
1620
|
+
|
1621
|
+
|
1622
|
+
/* Encode a serial object with quoted-printable encoding. */
|
1623
|
+
char *cbquoteencode(const char *ptr, int size){
|
1624
|
+
const unsigned char *rp;
|
1625
|
+
char *buf, *wp;
|
1626
|
+
int i, cols;
|
1627
|
+
assert(ptr);
|
1628
|
+
if(size < 0) size = strlen(ptr);
|
1629
|
+
rp = (const unsigned char *)ptr;
|
1630
|
+
buf = wp = cbmalloc(size * 3 + 1);
|
1631
|
+
cols = 0;
|
1632
|
+
for(i = 0; i < size; i++){
|
1633
|
+
if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') ||
|
1634
|
+
rp[i] > 0x7e){
|
1635
|
+
wp += sprintf(wp, "=%02X", rp[i]);
|
1636
|
+
cols += 3;
|
1637
|
+
} else {
|
1638
|
+
*(wp++) = rp[i];
|
1639
|
+
cols++;
|
1640
|
+
}
|
1641
|
+
}
|
1642
|
+
*wp = '\0';
|
1643
|
+
return buf;
|
1644
|
+
}
|
1645
|
+
|
1646
|
+
|
1647
|
+
/* Decode a string encoded with quoted-printable encoding. */
|
1648
|
+
char *cbquotedecode(const char *str, int *sp){
|
1649
|
+
char *buf, *wp;
|
1650
|
+
assert(str);
|
1651
|
+
buf = wp = cbmalloc(strlen(str) + 1);
|
1652
|
+
for(; *str != '\0'; str++){
|
1653
|
+
if(*str == '='){
|
1654
|
+
str++;
|
1655
|
+
if(*str == '\0'){
|
1656
|
+
break;
|
1657
|
+
} else if(str[0] == '\r' && str[1] == '\n'){
|
1658
|
+
str++;
|
1659
|
+
} else if(str[0] != '\n' && str[0] != '\r'){
|
1660
|
+
if(*str >= 'A' && *str <= 'Z'){
|
1661
|
+
*wp = (*str - 'A' + 10) * 16;
|
1662
|
+
} else if(*str >= 'a' && *str <= 'z'){
|
1663
|
+
*wp = (*str - 'a' + 10) * 16;
|
1664
|
+
} else {
|
1665
|
+
*wp = (*str - '0') * 16;
|
1666
|
+
}
|
1667
|
+
str++;
|
1668
|
+
if(*str == '\0') break;
|
1669
|
+
if(*str >= 'A' && *str <= 'Z'){
|
1670
|
+
*wp += *str - 'A' + 10;
|
1671
|
+
} else if(*str >= 'a' && *str <= 'z'){
|
1672
|
+
*wp += *str - 'a' + 10;
|
1673
|
+
} else {
|
1674
|
+
*wp += *str - '0';
|
1675
|
+
}
|
1676
|
+
wp++;
|
1677
|
+
}
|
1678
|
+
} else {
|
1679
|
+
*wp = *str;
|
1680
|
+
wp++;
|
1681
|
+
}
|
1682
|
+
}
|
1683
|
+
*wp = '\0';
|
1684
|
+
if(sp) *sp = wp - buf;
|
1685
|
+
return buf;
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
|
1689
|
+
/* Split a string of MIME into headers and the body. */
|
1690
|
+
char *cbmimebreak(const char *ptr, int size, CBMAP *attrs, int *sp){
|
1691
|
+
CBLIST *list;
|
1692
|
+
const char *head, *line, *pv, *ep;
|
1693
|
+
char *hbuf, *name;
|
1694
|
+
int i, j, wi, hlen;
|
1695
|
+
assert(ptr);
|
1696
|
+
if(size < 0) size = strlen(ptr);
|
1697
|
+
head = NULL;
|
1698
|
+
hlen = 0;
|
1699
|
+
for(i = 0; i < size; i++){
|
1700
|
+
if(i < size - 4 && ptr[i] == '\r' && ptr[i+1] == '\n' &&
|
1701
|
+
ptr[i+2] == '\r' && ptr[i+3] == '\n'){
|
1702
|
+
head = ptr;
|
1703
|
+
hlen = i;
|
1704
|
+
ptr += i + 4;
|
1705
|
+
size -= i + 4;
|
1706
|
+
break;
|
1707
|
+
} else if(i < size - 2 && ptr[i] == '\n' && ptr[i+1] == '\n'){
|
1708
|
+
head = ptr;
|
1709
|
+
hlen = i;
|
1710
|
+
ptr += i + 2;
|
1711
|
+
size -= i + 2;
|
1712
|
+
break;
|
1713
|
+
}
|
1714
|
+
}
|
1715
|
+
if(head && attrs){
|
1716
|
+
hbuf = cbmalloc(hlen + 1);
|
1717
|
+
wi = 0;
|
1718
|
+
for(i = 0; i < hlen; i++){
|
1719
|
+
if(head[i] == '\r') continue;
|
1720
|
+
if(i < hlen - 1 && head[i] == '\n' && (head[i+1] == ' ' || head[i+1] == '\t')){
|
1721
|
+
hbuf[wi++] = ' ';
|
1722
|
+
i++;
|
1723
|
+
} else {
|
1724
|
+
hbuf[wi++] = head[i];
|
1725
|
+
}
|
1726
|
+
}
|
1727
|
+
list = cbsplit(hbuf, wi, "\n");
|
1728
|
+
for(i = 0; i < cblistnum(list); i++){
|
1729
|
+
line = cblistval(list, i, NULL);
|
1730
|
+
if((pv = strchr(line, ':')) != NULL){
|
1731
|
+
name = cbmemdup(line, pv - line);
|
1732
|
+
for(j = 0; name[j] != '\0'; j++){
|
1733
|
+
if(name[j] >= 'A' && name[j] <= 'Z') name[j] -= 'A' - 'a';
|
1734
|
+
}
|
1735
|
+
pv++;
|
1736
|
+
while(*pv == ' ' || *pv == '\t'){
|
1737
|
+
pv++;
|
1738
|
+
}
|
1739
|
+
cbmapput(attrs, name, -1, pv, -1, TRUE);
|
1740
|
+
free(name);
|
1741
|
+
}
|
1742
|
+
|
1743
|
+
}
|
1744
|
+
cblistclose(list);
|
1745
|
+
free(hbuf);
|
1746
|
+
if((pv = cbmapget(attrs, "content-type", -1, NULL)) != NULL){
|
1747
|
+
if((ep = strchr(pv, ';')) != NULL){
|
1748
|
+
cbmapput(attrs, "TYPE", -1, pv, ep - pv, TRUE);
|
1749
|
+
do {
|
1750
|
+
ep++;
|
1751
|
+
while(ep[0] == ' '){
|
1752
|
+
ep++;
|
1753
|
+
}
|
1754
|
+
if(cbstrfwimatch(ep, "charset=")){
|
1755
|
+
ep += 8;
|
1756
|
+
if(ep[0] == '"') ep++;
|
1757
|
+
pv = ep;
|
1758
|
+
while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
|
1759
|
+
ep++;
|
1760
|
+
}
|
1761
|
+
cbmapput(attrs, "CHARSET", -1, pv, ep - pv, TRUE);
|
1762
|
+
} else if(cbstrfwimatch(ep, "boundary=")){
|
1763
|
+
ep += 9;
|
1764
|
+
if(ep[0] == '"') ep++;
|
1765
|
+
pv = ep;
|
1766
|
+
while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){
|
1767
|
+
ep++;
|
1768
|
+
}
|
1769
|
+
cbmapput(attrs, "BOUNDARY", -1, pv, ep - pv, TRUE);
|
1770
|
+
}
|
1771
|
+
} while((ep = strchr(ep, ';')) != NULL);
|
1772
|
+
} else {
|
1773
|
+
cbmapput(attrs, "TYPE", -1, pv, -1, TRUE);
|
1774
|
+
}
|
1775
|
+
}
|
1776
|
+
if((pv = cbmapget(attrs, "content-disposition", -1, NULL)) != NULL){
|
1777
|
+
if((ep = strchr(pv, ';')) != NULL){
|
1778
|
+
cbmapput(attrs, "DISPOSITION", -1, pv, ep - pv, TRUE);
|
1779
|
+
do {
|
1780
|
+
ep++;
|
1781
|
+
while(ep[0] == ' '){
|
1782
|
+
ep++;
|
1783
|
+
}
|
1784
|
+
if(cbstrfwimatch(ep, "filename=")){
|
1785
|
+
ep += 9;
|
1786
|
+
if(ep[0] == '"') ep++;
|
1787
|
+
pv = ep;
|
1788
|
+
while(ep[0] != '\0' && ep[0] != '"'){
|
1789
|
+
ep++;
|
1790
|
+
}
|
1791
|
+
cbmapput(attrs, "FILENAME", -1, pv, ep - pv, TRUE);
|
1792
|
+
} else if(cbstrfwimatch(ep, "name=")){
|
1793
|
+
ep += 5;
|
1794
|
+
if(ep[0] == '"') ep++;
|
1795
|
+
pv = ep;
|
1796
|
+
while(ep[0] != '\0' && ep[0] != '"'){
|
1797
|
+
ep++;
|
1798
|
+
}
|
1799
|
+
cbmapput(attrs, "NAME", -1, pv, ep - pv, TRUE);
|
1800
|
+
}
|
1801
|
+
} while((ep = strchr(ep, ';')) != NULL);
|
1802
|
+
} else {
|
1803
|
+
cbmapput(attrs, "DISPOSITION", -1, pv, -1, TRUE);
|
1804
|
+
}
|
1805
|
+
}
|
1806
|
+
}
|
1807
|
+
if(sp) *sp = size;
|
1808
|
+
return cbmemdup(ptr, size);
|
1809
|
+
}
|
1810
|
+
|
1811
|
+
|
1812
|
+
/* Split multipart data in MIME into its parts. */
|
1813
|
+
CBLIST *cbmimeparts(const char *ptr, int size, const char *boundary){
|
1814
|
+
CBLIST *list;
|
1815
|
+
const char *pv, *ep;
|
1816
|
+
int i, blen;
|
1817
|
+
assert(ptr && boundary);
|
1818
|
+
if(size < 0) size = strlen(ptr);
|
1819
|
+
list = cblistopen();
|
1820
|
+
blen = strlen(boundary);
|
1821
|
+
pv = NULL;
|
1822
|
+
for(i = 0; i < size; i++){
|
1823
|
+
if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
|
1824
|
+
cbstrfwmatch(ptr + i + 2, boundary)){
|
1825
|
+
pv = ptr + i + 2 + blen;
|
1826
|
+
if(*pv == '\r') pv++;
|
1827
|
+
if(*pv == '\n') pv++;
|
1828
|
+
size -= pv - ptr;
|
1829
|
+
ptr = pv;
|
1830
|
+
break;
|
1831
|
+
}
|
1832
|
+
}
|
1833
|
+
if(!pv) return list;
|
1834
|
+
for(i = 0; i < size; i++){
|
1835
|
+
if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size &&
|
1836
|
+
cbstrfwmatch(ptr + i + 2, boundary)){
|
1837
|
+
ep = ptr + i;
|
1838
|
+
if(ep > ptr && ep[-1] == '\n') ep--;
|
1839
|
+
if(ep > ptr && ep[-1] == '\r') ep--;
|
1840
|
+
cblistpush(list, pv, ep - pv);
|
1841
|
+
pv = ptr + i + 2 + blen;
|
1842
|
+
if(*pv == '\r') pv++;
|
1843
|
+
if(*pv == '\n') pv++;
|
1844
|
+
}
|
1845
|
+
}
|
1846
|
+
return list;
|
1847
|
+
}
|
1848
|
+
|
1849
|
+
|
1850
|
+
/* Encode a string with MIME encoding. */
|
1851
|
+
char *cbmimeencode(const char *str, const char *encname, int base){
|
1852
|
+
char *buf, *wp, *enc;
|
1853
|
+
int len;
|
1854
|
+
assert(str && encname);
|
1855
|
+
len = strlen(str);
|
1856
|
+
buf = cbmalloc(len * 3 + strlen(encname) + 16);
|
1857
|
+
wp = buf;
|
1858
|
+
wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
|
1859
|
+
enc = base ? cbbaseencode(str, len) : cbquoteencode(str, len);
|
1860
|
+
wp += sprintf(wp, "%s?=", enc);
|
1861
|
+
free(enc);
|
1862
|
+
return buf;
|
1863
|
+
}
|
1864
|
+
|
1865
|
+
|
1866
|
+
/* Decode a string encoded with MIME encoding. */
|
1867
|
+
char *cbmimedecode(const char *str, char *enp){
|
1868
|
+
char *buf, *wp, *tmp, *dec;
|
1869
|
+
const char *pv, *ep;
|
1870
|
+
int quoted;
|
1871
|
+
assert(str);
|
1872
|
+
if(enp) sprintf(enp, "US-ASCII");
|
1873
|
+
buf = cbmalloc(strlen(str) + 1);
|
1874
|
+
wp = buf;
|
1875
|
+
while(*str != '\0'){
|
1876
|
+
if(cbstrfwmatch(str, "=?")){
|
1877
|
+
str += 2;
|
1878
|
+
pv = str;
|
1879
|
+
if(!(ep = strchr(str, '?'))) continue;
|
1880
|
+
if(enp && ep - pv < CB_ENCBUFSIZ){
|
1881
|
+
memcpy(enp, pv, ep - pv);
|
1882
|
+
enp[ep-pv] = '\0';
|
1883
|
+
}
|
1884
|
+
pv = ep + 1;
|
1885
|
+
quoted = (*pv == 'Q' || *pv == 'q');
|
1886
|
+
if(*pv != '\0') pv++;
|
1887
|
+
if(*pv != '\0') pv++;
|
1888
|
+
if(!(ep = strchr(pv, '?'))) continue;
|
1889
|
+
tmp = cbmemdup(pv, ep - pv);
|
1890
|
+
dec = quoted ? cbquotedecode(tmp, NULL) : cbbasedecode(tmp, NULL);
|
1891
|
+
wp += sprintf(wp, "%s", dec);
|
1892
|
+
free(dec);
|
1893
|
+
free(tmp);
|
1894
|
+
str = ep + 1;
|
1895
|
+
if(*str != '\0') str++;
|
1896
|
+
} else {
|
1897
|
+
*(wp++) = *str;
|
1898
|
+
str++;
|
1899
|
+
}
|
1900
|
+
}
|
1901
|
+
*wp = '\0';
|
1902
|
+
return buf;
|
1903
|
+
}
|
1904
|
+
|
1905
|
+
|
1906
|
+
/* Split a string of CSV into rows. */
|
1907
|
+
CBLIST *cbcsvrows(const char *str){
|
1908
|
+
CBLIST *list;
|
1909
|
+
const char *pv;
|
1910
|
+
int quoted;
|
1911
|
+
assert(str);
|
1912
|
+
list = cblistopen();
|
1913
|
+
pv = str;
|
1914
|
+
quoted = FALSE;
|
1915
|
+
while(TRUE){
|
1916
|
+
if(*str == '"') quoted = !quoted;
|
1917
|
+
if(!quoted && (*str == '\r' || *str == '\n')){
|
1918
|
+
cblistpush(list, pv, str - pv);
|
1919
|
+
if(str[0] == '\r' && str[1] == '\n') str++;
|
1920
|
+
str++;
|
1921
|
+
pv = str;
|
1922
|
+
} else if(*str == '\0'){
|
1923
|
+
if(str > pv) cblistpush(list, pv, str - pv);
|
1924
|
+
break;
|
1925
|
+
} else {
|
1926
|
+
str++;
|
1927
|
+
}
|
1928
|
+
}
|
1929
|
+
return list;
|
1930
|
+
}
|
1931
|
+
|
1932
|
+
|
1933
|
+
/* Split a string of a row of CSV into cells. */
|
1934
|
+
CBLIST *cbcsvcells(const char *str){
|
1935
|
+
CBLIST *list, *uelist;
|
1936
|
+
const char *pv;
|
1937
|
+
char *tmp;
|
1938
|
+
int i, quoted;
|
1939
|
+
assert(str);
|
1940
|
+
list = cblistopen();
|
1941
|
+
pv = str;
|
1942
|
+
quoted = FALSE;
|
1943
|
+
while(TRUE){
|
1944
|
+
if(*str == '"') quoted = !quoted;
|
1945
|
+
if(!quoted && *str == ','){
|
1946
|
+
cblistpush(list, pv, str - pv);
|
1947
|
+
str++;
|
1948
|
+
pv = str;
|
1949
|
+
} else if(*str == '\0'){
|
1950
|
+
cblistpush(list, pv, str - pv);
|
1951
|
+
break;
|
1952
|
+
} else {
|
1953
|
+
str++;
|
1954
|
+
}
|
1955
|
+
}
|
1956
|
+
uelist = cblistopen();
|
1957
|
+
for(i = 0; i < cblistnum(list); i++){
|
1958
|
+
tmp = cbcsvunescape(cblistval(list, i, NULL));
|
1959
|
+
cblistpush(uelist, tmp, -1);
|
1960
|
+
free(tmp);
|
1961
|
+
}
|
1962
|
+
cblistclose(list);
|
1963
|
+
return uelist;
|
1964
|
+
}
|
1965
|
+
|
1966
|
+
|
1967
|
+
/* Escape a string with the meta characters of CSV. */
|
1968
|
+
char *cbcsvescape(const char *str){
|
1969
|
+
char *buf, *wp;
|
1970
|
+
int i;
|
1971
|
+
assert(str);
|
1972
|
+
buf = cbmalloc(strlen(str) * 2 + 3);
|
1973
|
+
wp = buf;
|
1974
|
+
*(wp++) = '"';
|
1975
|
+
for(i = 0; str[i] != '\0'; i++){
|
1976
|
+
if(str[i] == '"') *(wp++) = '"';
|
1977
|
+
*(wp++) = str[i];
|
1978
|
+
}
|
1979
|
+
*(wp++) = '"';
|
1980
|
+
*wp = '\0';
|
1981
|
+
return buf;
|
1982
|
+
}
|
1983
|
+
|
1984
|
+
|
1985
|
+
/* Unescape a string with the escaped meta characters of CSV. */
|
1986
|
+
char *cbcsvunescape(const char *str){
|
1987
|
+
char *buf, *wp;
|
1988
|
+
int i, len;
|
1989
|
+
assert(str);
|
1990
|
+
len = strlen(str);
|
1991
|
+
if(str[0] == '"'){
|
1992
|
+
str++;
|
1993
|
+
len--;
|
1994
|
+
if(str[len-1] == '"') len--;
|
1995
|
+
}
|
1996
|
+
buf = cbmalloc(len + 1);
|
1997
|
+
wp = buf;
|
1998
|
+
for(i = 0; i < len; i++){
|
1999
|
+
if(str[i] == '"'){
|
2000
|
+
if(str[i+1] == '"') *(wp++) = str[i++];
|
2001
|
+
} else {
|
2002
|
+
*(wp++) = str[i];
|
2003
|
+
}
|
2004
|
+
}
|
2005
|
+
*wp = '\0';
|
2006
|
+
return buf;
|
2007
|
+
}
|
2008
|
+
|
2009
|
+
|
2010
|
+
/* Split a string of XML into tags and text sections. */
|
2011
|
+
CBLIST *cbxmlbreak(const char *str, int cr){
|
2012
|
+
CBLIST *list;
|
2013
|
+
CBDATUM *datum;
|
2014
|
+
int i, pv, tag;
|
2015
|
+
char *ep;
|
2016
|
+
assert(str);
|
2017
|
+
list = cblistopen();
|
2018
|
+
i = 0;
|
2019
|
+
pv = 0;
|
2020
|
+
tag = FALSE;
|
2021
|
+
while(TRUE){
|
2022
|
+
if(str[i] == '\0'){
|
2023
|
+
if(i > pv) cblistpush(list, str + pv, i - pv);
|
2024
|
+
break;
|
2025
|
+
} else if(cbstrfwimatch(str + i, "<!--")){
|
2026
|
+
if(i > pv) cblistpush(list, str + pv, i - pv);
|
2027
|
+
if((ep = strstr(str + i, "-->")) != NULL){
|
2028
|
+
if(!cr) cblistpush(list, str + i, ep - str - i + 3);
|
2029
|
+
i = ep - str + 2;
|
2030
|
+
pv = i + 1;
|
2031
|
+
}
|
2032
|
+
} else if(cbstrfwimatch(str + i, "<![CDATA[")){
|
2033
|
+
if(i > pv) cblistpush(list, str + pv, i - pv);
|
2034
|
+
if((ep = strstr(str + i, "]]>")) != NULL){
|
2035
|
+
i += 9;
|
2036
|
+
datum = cbdatumopen(NULL, 0);
|
2037
|
+
while(str + i < ep){
|
2038
|
+
if(str[i] == '&'){
|
2039
|
+
cbdatumcat(datum, "&", 5);
|
2040
|
+
} else if(str[i] == '<'){
|
2041
|
+
cbdatumcat(datum, "<", 4);
|
2042
|
+
} else if(str[i] == '>'){
|
2043
|
+
cbdatumcat(datum, ">", 4);
|
2044
|
+
} else {
|
2045
|
+
cbdatumcat(datum, str + i, 1);
|
2046
|
+
}
|
2047
|
+
i++;
|
2048
|
+
}
|
2049
|
+
if(cbdatumsize(datum) > 0) cblistpush(list, cbdatumptr(datum), cbdatumsize(datum));
|
2050
|
+
cbdatumclose(datum);
|
2051
|
+
i = ep - str + 2;
|
2052
|
+
pv = i + 1;
|
2053
|
+
}
|
2054
|
+
} else if(!tag && str[i] == '<'){
|
2055
|
+
if(i > pv) cblistpush(list, str + pv, i - pv);
|
2056
|
+
tag = TRUE;
|
2057
|
+
pv = i;
|
2058
|
+
} else if(tag && str[i] == '>'){
|
2059
|
+
if(i > pv) cblistpush(list, str + pv, i - pv + 1);
|
2060
|
+
tag = FALSE;
|
2061
|
+
pv = i + 1;
|
2062
|
+
}
|
2063
|
+
i++;
|
2064
|
+
}
|
2065
|
+
return list;
|
2066
|
+
}
|
2067
|
+
|
2068
|
+
|
2069
|
+
/* Get the map of attributes of a XML tag. */
|
2070
|
+
CBMAP *cbxmlattrs(const char *str){
|
2071
|
+
CBMAP *map;
|
2072
|
+
const unsigned char *rp, *key, *val;
|
2073
|
+
char *copy, *raw;
|
2074
|
+
int ksiz, vsiz;
|
2075
|
+
assert(str);
|
2076
|
+
map = cbmapopenex(CB_MAPPBNUM);
|
2077
|
+
rp = (unsigned char *)str;
|
2078
|
+
while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
|
2079
|
+
rp++;
|
2080
|
+
}
|
2081
|
+
key = rp;
|
2082
|
+
while(*rp > 0x20 && *rp != '/' && *rp != '>'){
|
2083
|
+
rp++;
|
2084
|
+
}
|
2085
|
+
cbmapput(map, "", -1, (char *)key, rp - key, FALSE);
|
2086
|
+
while(*rp != '\0'){
|
2087
|
+
while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
|
2088
|
+
rp++;
|
2089
|
+
}
|
2090
|
+
key = rp;
|
2091
|
+
while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
|
2092
|
+
rp++;
|
2093
|
+
}
|
2094
|
+
ksiz = rp - key;
|
2095
|
+
while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
|
2096
|
+
rp++;
|
2097
|
+
}
|
2098
|
+
if(*rp == '"'){
|
2099
|
+
rp++;
|
2100
|
+
val = rp;
|
2101
|
+
while(*rp != '\0' && *rp != '"'){
|
2102
|
+
rp++;
|
2103
|
+
}
|
2104
|
+
vsiz = rp - val;
|
2105
|
+
} else if(*rp == '\''){
|
2106
|
+
rp++;
|
2107
|
+
val = rp;
|
2108
|
+
while(*rp != '\0' && *rp != '\''){
|
2109
|
+
rp++;
|
2110
|
+
}
|
2111
|
+
vsiz = rp - val;
|
2112
|
+
} else {
|
2113
|
+
val = rp;
|
2114
|
+
while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '/' && *rp != '>'){
|
2115
|
+
rp++;
|
2116
|
+
}
|
2117
|
+
vsiz = rp - val;
|
2118
|
+
}
|
2119
|
+
if(*rp != '\0') rp++;
|
2120
|
+
if(ksiz > 0){
|
2121
|
+
copy = cbmemdup((char *)val, vsiz);
|
2122
|
+
raw = cbxmlunescape(copy);
|
2123
|
+
cbmapput(map, (char *)key, ksiz, raw, -1, FALSE);
|
2124
|
+
free(raw);
|
2125
|
+
free(copy);
|
2126
|
+
}
|
2127
|
+
}
|
2128
|
+
return map;
|
2129
|
+
}
|
2130
|
+
|
2131
|
+
|
2132
|
+
/* Escape a string with the meta characters of XML. */
|
2133
|
+
char *cbxmlescape(const char *str){
|
2134
|
+
CBDATUM *datum;
|
2135
|
+
assert(str);
|
2136
|
+
datum = cbdatumopen("", 0);
|
2137
|
+
while(*str != '\0'){
|
2138
|
+
switch(*str){
|
2139
|
+
case '&':
|
2140
|
+
cbdatumcat(datum, "&", 5);
|
2141
|
+
break;
|
2142
|
+
case '<':
|
2143
|
+
cbdatumcat(datum, "<", 4);
|
2144
|
+
break;
|
2145
|
+
case '>':
|
2146
|
+
cbdatumcat(datum, ">", 4);
|
2147
|
+
break;
|
2148
|
+
case '"':
|
2149
|
+
cbdatumcat(datum, """, 6);
|
2150
|
+
break;
|
2151
|
+
case '\'':
|
2152
|
+
cbdatumcat(datum, "'", 6);
|
2153
|
+
break;
|
2154
|
+
default:
|
2155
|
+
cbdatumcat(datum, str, 1);
|
2156
|
+
break;
|
2157
|
+
}
|
2158
|
+
str++;
|
2159
|
+
}
|
2160
|
+
return cbdatumtomalloc(datum, NULL);
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
|
2164
|
+
/* Unescape a string with the entity references of XML. */
|
2165
|
+
char *cbxmlunescape(const char *str){
|
2166
|
+
CBDATUM *datum;
|
2167
|
+
assert(str);
|
2168
|
+
datum = cbdatumopen("", 0);
|
2169
|
+
while(*str != '\0'){
|
2170
|
+
if(*str == '&'){
|
2171
|
+
if(cbstrfwmatch(str, "&")){
|
2172
|
+
cbdatumcat(datum, "&", 1);
|
2173
|
+
str += 5;
|
2174
|
+
} else if(cbstrfwmatch(str, "<")){
|
2175
|
+
cbdatumcat(datum, "<", 1);
|
2176
|
+
str += 4;
|
2177
|
+
} else if(cbstrfwmatch(str, ">")){
|
2178
|
+
cbdatumcat(datum, ">", 1);
|
2179
|
+
str += 4;
|
2180
|
+
} else if(cbstrfwmatch(str, """)){
|
2181
|
+
cbdatumcat(datum, "\"", 1);
|
2182
|
+
str += 6;
|
2183
|
+
} else if(cbstrfwmatch(str, "'")){
|
2184
|
+
cbdatumcat(datum, "'", 1);
|
2185
|
+
str += 6;
|
2186
|
+
} else {
|
2187
|
+
cbdatumcat(datum, str, 1);
|
2188
|
+
str++;
|
2189
|
+
}
|
2190
|
+
} else {
|
2191
|
+
cbdatumcat(datum, str, 1);
|
2192
|
+
str++;
|
2193
|
+
}
|
2194
|
+
}
|
2195
|
+
return cbdatumtomalloc(datum, NULL);
|
2196
|
+
}
|
2197
|
+
|
2198
|
+
|
2199
|
+
/* Compress a serial object with ZLIB. */
|
2200
|
+
char *cbdeflate(const char *ptr, int size, int *sp){
|
2201
|
+
assert(ptr && sp);
|
2202
|
+
if(!_qdbm_deflate) return NULL;
|
2203
|
+
return _qdbm_deflate(ptr, size, sp);
|
2204
|
+
}
|
2205
|
+
|
2206
|
+
|
2207
|
+
/* Decompress a serial object compressed with ZLIB. */
|
2208
|
+
char *cbinflate(const char *ptr, int size, int *sp){
|
2209
|
+
assert(ptr && size >= 0);
|
2210
|
+
if(!_qdbm_inflate) return NULL;
|
2211
|
+
return _qdbm_inflate(ptr, size, sp);
|
2212
|
+
}
|
2213
|
+
|
2214
|
+
|
2215
|
+
/* Get the CRC32 checksum of a serial object. */
|
2216
|
+
unsigned int cbgetcrc(const char *ptr, int size){
|
2217
|
+
assert(ptr);
|
2218
|
+
if(!_qdbm_inflate) return 0;
|
2219
|
+
return _qdbm_getcrc(ptr, size);
|
2220
|
+
}
|
2221
|
+
|
2222
|
+
|
2223
|
+
/* Convert the character encoding of a string. */
|
2224
|
+
char *cbiconv(const char *ptr, int size, const char *icode, const char *ocode, int *sp, int *mp){
|
2225
|
+
assert(ptr && icode && ocode);
|
2226
|
+
if(!_qdbm_iconv) return NULL;
|
2227
|
+
return _qdbm_iconv(ptr, size, icode, ocode, sp, mp);
|
2228
|
+
}
|
2229
|
+
|
2230
|
+
|
2231
|
+
/* Detect the encoding of a string automatically. */
|
2232
|
+
const char *cbencname(const char *ptr, int size){
|
2233
|
+
assert(ptr);
|
2234
|
+
if(!_qdbm_encname) return "ISO-8859-1";
|
2235
|
+
return _qdbm_encname(ptr, size);
|
2236
|
+
}
|
2237
|
+
|
2238
|
+
|
2239
|
+
/* Get the jet lag of the local time in seconds. */
|
2240
|
+
int cbjetlag(void){
|
2241
|
+
struct tm ts, *tp;
|
2242
|
+
time_t t, gt, lt;
|
2243
|
+
if((t = time(NULL)) < 0) return 0;
|
2244
|
+
if(!(tp = _qdbm_gmtime(&t, &ts))) return 0;
|
2245
|
+
if((gt = mktime(tp)) < 0) return 0;
|
2246
|
+
if(!(tp = _qdbm_localtime(&t, &ts))) return 0;
|
2247
|
+
if((lt = mktime(tp)) < 0) return 0;
|
2248
|
+
return lt - gt;
|
2249
|
+
}
|
2250
|
+
|
2251
|
+
|
2252
|
+
/* Get the Gregorian calendar of a time. */
|
2253
|
+
void cbcalendar(time_t t, int jl, int *yearp, int *monp, int *dayp,
|
2254
|
+
int *hourp, int *minp, int *secp){
|
2255
|
+
struct tm ts, *tp;
|
2256
|
+
if(t < 0) t = time(NULL);
|
2257
|
+
t += jl;
|
2258
|
+
if(!(tp = _qdbm_gmtime(&t, &ts))) return;
|
2259
|
+
if(yearp) *yearp = tp->tm_year + 1900;
|
2260
|
+
if(monp) *monp = tp->tm_mon + 1;
|
2261
|
+
if(dayp) *dayp = tp->tm_mday;
|
2262
|
+
if(hourp) *hourp = tp->tm_hour;
|
2263
|
+
if(minp) *minp = tp->tm_min;
|
2264
|
+
if(secp) *secp = tp->tm_sec;
|
2265
|
+
}
|
2266
|
+
|
2267
|
+
|
2268
|
+
/* Get the day of week of a date. */
|
2269
|
+
int cbdayofweek(int year, int mon, int day){
|
2270
|
+
if(mon < 3){
|
2271
|
+
year--;
|
2272
|
+
mon += 12;
|
2273
|
+
}
|
2274
|
+
return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7;
|
2275
|
+
}
|
2276
|
+
|
2277
|
+
|
2278
|
+
/* Get the string for a date in W3CDTF. */
|
2279
|
+
char *cbdatestrwww(time_t t, int jl){
|
2280
|
+
char date[CB_DATEBUFSIZ], tzone[CB_DATEBUFSIZ];
|
2281
|
+
int year, mon, day, hour, min, sec;
|
2282
|
+
cbcalendar(t, jl, &year, &mon, &day, &hour, &min, &sec);
|
2283
|
+
jl /= 60;
|
2284
|
+
if(jl == 0){
|
2285
|
+
sprintf(tzone, "Z");
|
2286
|
+
} else if(jl < 0){
|
2287
|
+
jl *= -1;
|
2288
|
+
sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60);
|
2289
|
+
} else {
|
2290
|
+
sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60);
|
2291
|
+
}
|
2292
|
+
sprintf(date, "%04d-%02d-%02dT%02d:%02d:%02d%s", year, mon, day, hour, min, sec, tzone);
|
2293
|
+
return cbmemdup(date, -1);
|
2294
|
+
}
|
2295
|
+
|
2296
|
+
|
2297
|
+
/* Get the string for a date in RFC 1123 format. */
|
2298
|
+
char *cbdatestrhttp(time_t t, int jl){
|
2299
|
+
char date[CB_DATEBUFSIZ], *wp;
|
2300
|
+
int year, mon, day, hour, min, sec;
|
2301
|
+
cbcalendar(t, jl, &year, &mon, &day, &hour, &min, &sec);
|
2302
|
+
jl /= 60;
|
2303
|
+
wp = date;
|
2304
|
+
switch(cbdayofweek(year, mon, day)){
|
2305
|
+
case 0: wp += sprintf(wp, "Sun, "); break;
|
2306
|
+
case 1: wp += sprintf(wp, "Mon, "); break;
|
2307
|
+
case 2: wp += sprintf(wp, "Tue, "); break;
|
2308
|
+
case 3: wp += sprintf(wp, "Wed, "); break;
|
2309
|
+
case 4: wp += sprintf(wp, "Thu, "); break;
|
2310
|
+
case 5: wp += sprintf(wp, "Fri, "); break;
|
2311
|
+
case 6: wp += sprintf(wp, "Sat, "); break;
|
2312
|
+
}
|
2313
|
+
wp += sprintf(wp, "%02d ", day);
|
2314
|
+
switch(mon){
|
2315
|
+
case 1: wp += sprintf(wp, "Jan "); break;
|
2316
|
+
case 2: wp += sprintf(wp, "Feb "); break;
|
2317
|
+
case 3: wp += sprintf(wp, "Mar "); break;
|
2318
|
+
case 4: wp += sprintf(wp, "Apr "); break;
|
2319
|
+
case 5: wp += sprintf(wp, "May "); break;
|
2320
|
+
case 6: wp += sprintf(wp, "Jun "); break;
|
2321
|
+
case 7: wp += sprintf(wp, "Jul "); break;
|
2322
|
+
case 8: wp += sprintf(wp, "Aug "); break;
|
2323
|
+
case 9: wp += sprintf(wp, "Sep "); break;
|
2324
|
+
case 10: wp += sprintf(wp, "Oct "); break;
|
2325
|
+
case 11: wp += sprintf(wp, "Nov "); break;
|
2326
|
+
case 12: wp += sprintf(wp, "Dec "); break;
|
2327
|
+
}
|
2328
|
+
wp += sprintf(wp, "%04d %02d:%02d:%02d ", year, hour, min, sec);
|
2329
|
+
if(jl == 0){
|
2330
|
+
wp += sprintf(wp, "GMT");
|
2331
|
+
} else if(jl < 0){
|
2332
|
+
jl *= -1;
|
2333
|
+
wp += sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
|
2334
|
+
} else {
|
2335
|
+
wp += sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
|
2336
|
+
}
|
2337
|
+
return cbmemdup(date, -1);
|
2338
|
+
}
|
2339
|
+
|
2340
|
+
|
2341
|
+
/* Get the time value of a date string in decimal, W3CDTF, or RFC 1123. */
|
2342
|
+
time_t cbstrmktime(const char *str){
|
2343
|
+
char *pv, *rp;
|
2344
|
+
int len;
|
2345
|
+
time_t t;
|
2346
|
+
struct tm ts;
|
2347
|
+
assert(str);
|
2348
|
+
memset(&ts, 0, sizeof(struct tm));
|
2349
|
+
ts.tm_year = 70;
|
2350
|
+
ts.tm_mon = 0;
|
2351
|
+
ts.tm_mday = 1;
|
2352
|
+
ts.tm_hour = 0;
|
2353
|
+
ts.tm_min = 0;
|
2354
|
+
ts.tm_sec = 0;
|
2355
|
+
ts.tm_isdst = 0;
|
2356
|
+
len = strlen(str);
|
2357
|
+
t = (time_t)strtol(str, &pv, 10);
|
2358
|
+
if(*pv == '\0' && (t < 1900 || t > 2039)) return t;
|
2359
|
+
if(len == 4 || (len > 4 && str[4] == '-')){
|
2360
|
+
ts.tm_year = atoi(str) - 1900;
|
2361
|
+
if((pv = strchr(str, '-')) != NULL && pv - str == 4){
|
2362
|
+
rp = pv + 1;
|
2363
|
+
ts.tm_mon = atoi(rp) - 1;
|
2364
|
+
if((pv = strchr(rp, '-')) != NULL && pv - str == 7){
|
2365
|
+
rp = pv + 1;
|
2366
|
+
ts.tm_mday = atoi(rp);
|
2367
|
+
if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){
|
2368
|
+
rp = pv + 1;
|
2369
|
+
ts.tm_hour = atoi(rp);
|
2370
|
+
if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
|
2371
|
+
rp = pv + 1;
|
2372
|
+
ts.tm_min = atoi(rp);
|
2373
|
+
}
|
2374
|
+
if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
|
2375
|
+
rp = pv + 1;
|
2376
|
+
ts.tm_sec = atoi(rp);
|
2377
|
+
}
|
2378
|
+
if((pv = strchr(rp, '.')) != NULL && pv - str == 19) rp = pv + 1;
|
2379
|
+
strtol(rp, &pv, 10);
|
2380
|
+
if((*pv == '+' || *pv == '-') && strlen(pv) == 6 && pv[3] == ':')
|
2381
|
+
ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
|
2382
|
+
}
|
2383
|
+
}
|
2384
|
+
}
|
2385
|
+
ts.tm_sec += cbjetlag();
|
2386
|
+
return mktime(&ts);
|
2387
|
+
}
|
2388
|
+
if(len >= 25 && str[3] == ',' && str[4] == ' ' && str[7] == ' ' && str[11] == ' ' &&
|
2389
|
+
str[16] == ' ' && str[19] == ':' && str[22] == ':'){
|
2390
|
+
ts.tm_mday = atoi(str + 5);
|
2391
|
+
if(cbstrfwmatch(str + 8, "Jan")){
|
2392
|
+
ts.tm_mon = 0;
|
2393
|
+
} else if(cbstrfwmatch(str + 8, "Feb")){
|
2394
|
+
ts.tm_mon = 1;
|
2395
|
+
} else if(cbstrfwmatch(str + 8, "Mar")){
|
2396
|
+
ts.tm_mon = 2;
|
2397
|
+
} else if(cbstrfwmatch(str + 8, "Apr")){
|
2398
|
+
ts.tm_mon = 3;
|
2399
|
+
} else if(cbstrfwmatch(str + 8, "May")){
|
2400
|
+
ts.tm_mon = 4;
|
2401
|
+
} else if(cbstrfwmatch(str + 8, "Jun")){
|
2402
|
+
ts.tm_mon = 5;
|
2403
|
+
} else if(cbstrfwmatch(str + 8, "Jul")){
|
2404
|
+
ts.tm_mon = 6;
|
2405
|
+
} else if(cbstrfwmatch(str + 8, "Aug")){
|
2406
|
+
ts.tm_mon = 7;
|
2407
|
+
} else if(cbstrfwmatch(str + 8, "Sep")){
|
2408
|
+
ts.tm_mon = 8;
|
2409
|
+
} else if(cbstrfwmatch(str + 8, "Oct")){
|
2410
|
+
ts.tm_mon = 9;
|
2411
|
+
} else if(cbstrfwmatch(str + 8, "Nov")){
|
2412
|
+
ts.tm_mon = 10;
|
2413
|
+
} else if(cbstrfwmatch(str + 8, "Dec")){
|
2414
|
+
ts.tm_mon = 11;
|
2415
|
+
}
|
2416
|
+
ts.tm_year = atoi(str + 12) - 1900;
|
2417
|
+
ts.tm_hour = atoi(str + 17);
|
2418
|
+
ts.tm_min = atoi(str + 20);
|
2419
|
+
ts.tm_sec = atoi(str + 23);
|
2420
|
+
if(len == 31 && (str[26] == '+' || str[26] == '-')){
|
2421
|
+
ts.tm_sec -= ((str[27] - '0') * 36000 + (str[28] - '0') * 3600 +
|
2422
|
+
(str[29] - '0') * 600 + (str[30] - '0') * 60) * (str[26] == '+' ? 1 : -1);
|
2423
|
+
} else if(len > 26){
|
2424
|
+
if(!strcmp(str + 26, "JST")){
|
2425
|
+
ts.tm_sec -= 9 * 3600;
|
2426
|
+
} else if(!strcmp(str + 26, "CCT")){
|
2427
|
+
ts.tm_sec -= 8 * 3600;
|
2428
|
+
} else if(!strcmp(str + 26, "KST")){
|
2429
|
+
ts.tm_sec -= 9 * 3600;
|
2430
|
+
} else if(!strcmp(str + 26, "EDT")){
|
2431
|
+
ts.tm_sec -= -4 * 3600;
|
2432
|
+
} else if(!strcmp(str + 26, "EST")){
|
2433
|
+
ts.tm_sec -= -5 * 3600;
|
2434
|
+
} else if(!strcmp(str + 26, "CDT")){
|
2435
|
+
ts.tm_sec -= -5 * 3600;
|
2436
|
+
} else if(!strcmp(str + 26, "CST")){
|
2437
|
+
ts.tm_sec -= -6 * 3600;
|
2438
|
+
} else if(!strcmp(str + 26, "MDT")){
|
2439
|
+
ts.tm_sec -= -6 * 3600;
|
2440
|
+
} else if(!strcmp(str + 26, "MST")){
|
2441
|
+
ts.tm_sec -= -7 * 3600;
|
2442
|
+
} else if(!strcmp(str + 26, "PDT")){
|
2443
|
+
ts.tm_sec -= -7 * 3600;
|
2444
|
+
} else if(!strcmp(str + 26, "PST")){
|
2445
|
+
ts.tm_sec -= -8 * 3600;
|
2446
|
+
} else if(!strcmp(str + 26, "HDT")){
|
2447
|
+
ts.tm_sec -= -9 * 3600;
|
2448
|
+
} else if(!strcmp(str + 26, "HST")){
|
2449
|
+
ts.tm_sec -= -10 * 3600;
|
2450
|
+
}
|
2451
|
+
}
|
2452
|
+
ts.tm_sec += cbjetlag();
|
2453
|
+
return mktime(&ts);
|
2454
|
+
}
|
2455
|
+
return -1;
|
2456
|
+
}
|
2457
|
+
|
2458
|
+
|
2459
|
+
/* Get user and system processing times. */
|
2460
|
+
void cbproctime(double *usrp, double *sysp){
|
2461
|
+
struct tms buf;
|
2462
|
+
times(&buf);
|
2463
|
+
if(usrp) *usrp = (double)buf.tms_utime / sysconf(_SC_CLK_TCK);
|
2464
|
+
if(sysp) *sysp = (double)buf.tms_stime / sysconf(_SC_CLK_TCK);
|
2465
|
+
}
|
2466
|
+
|
2467
|
+
|
2468
|
+
/* Ensure that the standard I/O is binary mode. */
|
2469
|
+
void cbstdiobin(void){
|
2470
|
+
if(setmode(0, O_BINARY) == -1 || setmode(1, O_BINARY) == -1 || setmode(2, O_BINARY) == -1){
|
2471
|
+
if(cbfatalfunc){
|
2472
|
+
cbfatalfunc("setmode failed");
|
2473
|
+
} else {
|
2474
|
+
cbmyfatal("setmode failed");
|
2475
|
+
}
|
2476
|
+
}
|
2477
|
+
}
|
2478
|
+
|
2479
|
+
|
2480
|
+
|
2481
|
+
/*************************************************************************************************
|
2482
|
+
* features for experts
|
2483
|
+
*************************************************************************************************/
|
2484
|
+
|
2485
|
+
|
2486
|
+
/* Get a map handle with specifying the number of buckets. */
|
2487
|
+
CBMAP *cbmapopenex(int bnum){
|
2488
|
+
CBMAP *map;
|
2489
|
+
int i;
|
2490
|
+
assert(bnum > 0);
|
2491
|
+
map = cbmalloc(sizeof(*map));
|
2492
|
+
map->buckets = cbmalloc(sizeof(map->buckets[0]) * bnum);
|
2493
|
+
for(i = 0; i < bnum; i++){
|
2494
|
+
map->buckets[i] = NULL;
|
2495
|
+
}
|
2496
|
+
map->first = NULL;
|
2497
|
+
map->last = NULL;
|
2498
|
+
map->cur = NULL;
|
2499
|
+
map->bnum = bnum;
|
2500
|
+
map->rnum = 0;
|
2501
|
+
return map;
|
2502
|
+
}
|
2503
|
+
|
2504
|
+
|
2505
|
+
|
2506
|
+
/*************************************************************************************************
|
2507
|
+
* private objects
|
2508
|
+
*************************************************************************************************/
|
2509
|
+
|
2510
|
+
|
2511
|
+
/* Show error message on the standard error output and exit.
|
2512
|
+
`message' specifies an error message. */
|
2513
|
+
static void cbmyfatal(const char *message){
|
2514
|
+
char buf[CB_MSGBUFSIZ];
|
2515
|
+
assert(message);
|
2516
|
+
sprintf(buf, "fatal error: %s\n", message);
|
2517
|
+
write(2, buf, strlen(buf));
|
2518
|
+
exit(1);
|
2519
|
+
}
|
2520
|
+
|
2521
|
+
|
2522
|
+
/* Handler to invoke the global garbage collector. */
|
2523
|
+
static void cbggchandler(void){
|
2524
|
+
cbggckeeper(NULL, NULL);
|
2525
|
+
}
|
2526
|
+
|
2527
|
+
|
2528
|
+
/* Manage resources of the global garbage collector.
|
2529
|
+
`ptr' specifies the pointer to add to the collection. If it is `NULL', all resources are
|
2530
|
+
released.
|
2531
|
+
`func' specifies the pointer to the function to release the resources. */
|
2532
|
+
static void cbggckeeper(void *ptr, void (*func)(void *)){
|
2533
|
+
static void **parray = NULL;
|
2534
|
+
static void (**farray)(void *) = NULL;
|
2535
|
+
static int onum = 0;
|
2536
|
+
static int asiz = CB_GCUNIT;
|
2537
|
+
int i;
|
2538
|
+
if(!ptr){
|
2539
|
+
if(!parray) return;
|
2540
|
+
for(i = onum - 1; i >= 0; i--){
|
2541
|
+
farray[i](parray[i]);
|
2542
|
+
}
|
2543
|
+
free(parray);
|
2544
|
+
free(farray);
|
2545
|
+
parray = NULL;
|
2546
|
+
farray = NULL;
|
2547
|
+
onum = 0;
|
2548
|
+
asiz = CB_GCUNIT;
|
2549
|
+
return;
|
2550
|
+
}
|
2551
|
+
if(!parray){
|
2552
|
+
parray = cbmalloc(sizeof(void *) * asiz);
|
2553
|
+
farray = cbmalloc(sizeof(void *) * asiz);
|
2554
|
+
if(atexit(cbggchandler) != 0){
|
2555
|
+
if(cbfatalfunc){
|
2556
|
+
cbfatalfunc("gc failed");
|
2557
|
+
} else {
|
2558
|
+
cbmyfatal("gc failed");
|
2559
|
+
}
|
2560
|
+
}
|
2561
|
+
}
|
2562
|
+
if(onum >= asiz){
|
2563
|
+
asiz *= 2;
|
2564
|
+
parray = cbrealloc(parray, sizeof(void *) * asiz);
|
2565
|
+
farray = cbrealloc(farray, sizeof(void *) * asiz);
|
2566
|
+
}
|
2567
|
+
parray[onum] = ptr;
|
2568
|
+
farray[onum] = func;
|
2569
|
+
onum++;
|
2570
|
+
}
|
2571
|
+
|
2572
|
+
|
2573
|
+
/* Utility function for quick sort.
|
2574
|
+
`bp' specifies the pointer to the pointer to an array.
|
2575
|
+
`nmemb' specifies the number of elements of the array.
|
2576
|
+
`size' specifies the size of each element.
|
2577
|
+
`pswap' specifies the pointer to the swap region for a pivot.
|
2578
|
+
`vswap' specifies the pointer to the swap region for elements.
|
2579
|
+
`compar' specifies the pointer to comparing function. */
|
2580
|
+
static void cbqsortsub(char *bp, int nmemb, int size, char *pswap, char *vswap,
|
2581
|
+
int(*compar)(const void *, const void *)){
|
2582
|
+
int top, bottom;
|
2583
|
+
assert(bp && nmemb >= 0 && size > 0 && pswap && vswap && compar);
|
2584
|
+
if(nmemb < 10){
|
2585
|
+
if(nmemb > 1) cbisort(bp, nmemb, size, compar);
|
2586
|
+
return;
|
2587
|
+
}
|
2588
|
+
top = 0;
|
2589
|
+
bottom = nmemb - 1;
|
2590
|
+
memcpy(pswap, bp + (nmemb / 2) * size, size);
|
2591
|
+
while(top - 1 < bottom){
|
2592
|
+
if(compar(bp + top * size, pswap) < 0){
|
2593
|
+
top++;
|
2594
|
+
} else if(compar(bp + bottom * size, pswap) > 0){
|
2595
|
+
bottom--;
|
2596
|
+
} else {
|
2597
|
+
if(top != bottom){
|
2598
|
+
memcpy(vswap, bp + top * size, size);
|
2599
|
+
memcpy(bp + top * size, bp + bottom * size, size);
|
2600
|
+
memcpy(bp + bottom * size, vswap, size);
|
2601
|
+
}
|
2602
|
+
top++;
|
2603
|
+
bottom--;
|
2604
|
+
}
|
2605
|
+
}
|
2606
|
+
cbqsortsub(bp, top, size, pswap, vswap, compar);
|
2607
|
+
cbqsortsub(bp + (bottom + 1) * size, nmemb - bottom - 1, size, pswap, vswap, compar);
|
2608
|
+
}
|
2609
|
+
|
2610
|
+
|
2611
|
+
/* Compare two list elements.
|
2612
|
+
`a' specifies the pointer to one element.
|
2613
|
+
`b' specifies the pointer to the other element.
|
2614
|
+
The return value is positive if a is big, negative if b is big, else, it is 0. */
|
2615
|
+
static int cblistelemcmp(const void *a, const void *b){
|
2616
|
+
int i, size;
|
2617
|
+
CBLISTDATUM *ap, *bp;
|
2618
|
+
char *ao, *bo;
|
2619
|
+
assert(a && b);
|
2620
|
+
ap = (CBLISTDATUM *)a;
|
2621
|
+
bp = (CBLISTDATUM *)b;
|
2622
|
+
ao = ap->dptr;
|
2623
|
+
bo = bp->dptr;
|
2624
|
+
size = ap->dsize < bp->dsize ? ap->dsize : bp->dsize;
|
2625
|
+
for(i = 0; i < size; i++){
|
2626
|
+
if(ao[i] > bo[i]) return 1;
|
2627
|
+
if(ao[i] < bo[i]) return -1;
|
2628
|
+
}
|
2629
|
+
return ap->dsize - bp->dsize;
|
2630
|
+
}
|
2631
|
+
|
2632
|
+
|
2633
|
+
/* Get the first hash value.
|
2634
|
+
`kbuf' specifies the pointer to the region of a key.
|
2635
|
+
`ksiz' specifies the size of the key.
|
2636
|
+
The return value is 31 bit hash value of the key. */
|
2637
|
+
static int cbfirsthash(const char *kbuf, int ksiz){
|
2638
|
+
const unsigned char *p;
|
2639
|
+
unsigned int sum;
|
2640
|
+
int i;
|
2641
|
+
assert(kbuf && ksiz >= 0);
|
2642
|
+
p = (const unsigned char *)kbuf;
|
2643
|
+
sum = 751;
|
2644
|
+
for(i = 0; i < ksiz; i++){
|
2645
|
+
sum = sum * 31 + p[i];
|
2646
|
+
}
|
2647
|
+
return (sum * 87767623) & 0x7FFFFFFF;
|
2648
|
+
}
|
2649
|
+
|
2650
|
+
|
2651
|
+
/* Get the second hash value.
|
2652
|
+
`kbuf' specifies the pointer to the region of a key.
|
2653
|
+
`ksiz' specifies the size of the key.
|
2654
|
+
The return value is 31 bit hash value of the key. */
|
2655
|
+
static int cbsecondhash(const char *kbuf, int ksiz){
|
2656
|
+
const unsigned char *p;
|
2657
|
+
unsigned int sum;
|
2658
|
+
int i;
|
2659
|
+
assert(kbuf && ksiz >= 0);
|
2660
|
+
p = (const unsigned char *)kbuf;
|
2661
|
+
sum = 19780211;
|
2662
|
+
for(i = ksiz - 1; i >= 0; i--){
|
2663
|
+
sum = sum * 37 + p[i];
|
2664
|
+
}
|
2665
|
+
return (sum * 43321879) & 0x7FFFFFFF;
|
2666
|
+
}
|
2667
|
+
|
2668
|
+
|
2669
|
+
/* Compare two keys.
|
2670
|
+
`abuf' specifies the pointer to the region of the former.
|
2671
|
+
`asiz' specifies the size of the region.
|
2672
|
+
`bbuf' specifies the pointer to the region of the latter.
|
2673
|
+
`bsiz' specifies the size of the region.
|
2674
|
+
The return value is 0 if two equals, positive if the formar is big, else, negative. */
|
2675
|
+
static int cbkeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
|
2676
|
+
assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
|
2677
|
+
if(asiz > bsiz) return 1;
|
2678
|
+
if(asiz < bsiz) return -1;
|
2679
|
+
return memcmp(abuf, bbuf, asiz);
|
2680
|
+
}
|
2681
|
+
|
2682
|
+
|
2683
|
+
/* Set a buffer for a variable length number.
|
2684
|
+
`buf' specifies the pointer to the buffer.
|
2685
|
+
`num' specifies the number.
|
2686
|
+
The return value is the size of valid region. */
|
2687
|
+
static int cbsetvnumbuf(char *buf, int num){
|
2688
|
+
div_t d;
|
2689
|
+
int len;
|
2690
|
+
assert(buf && num >= 0);
|
2691
|
+
if(num == 0){
|
2692
|
+
((signed char *)buf)[0] = 0;
|
2693
|
+
return 1;
|
2694
|
+
}
|
2695
|
+
len = 0;
|
2696
|
+
while(num > 0){
|
2697
|
+
d = div(num, 128);
|
2698
|
+
num = d.quot;
|
2699
|
+
((signed char *)buf)[len] = d.rem;
|
2700
|
+
if(num > 0) ((signed char *)buf)[len] = -(((signed char *)buf)[len]) - 1;
|
2701
|
+
len++;
|
2702
|
+
}
|
2703
|
+
return len;
|
2704
|
+
}
|
2705
|
+
|
2706
|
+
|
2707
|
+
/* Read a variable length buffer.
|
2708
|
+
`buf' specifies the pointer to the buffer.
|
2709
|
+
`size' specifies the limit size to read.
|
2710
|
+
`sp' specifies the pointer to a variable to which the size of the read region assigned.
|
2711
|
+
The return value is the value of the buffer. */
|
2712
|
+
static int cbreadvnumbuf(const char *buf, int size, int *sp){
|
2713
|
+
int i, num, base;
|
2714
|
+
assert(buf && size > 0 && sp);
|
2715
|
+
num = 0;
|
2716
|
+
base = 1;
|
2717
|
+
if(size < 2){
|
2718
|
+
*sp = 1;
|
2719
|
+
return ((signed char *)buf)[0];
|
2720
|
+
}
|
2721
|
+
for(i = 0; i < size; i++){
|
2722
|
+
if(((signed char *)buf)[i] >= 0){
|
2723
|
+
num += ((signed char *)buf)[i] * base;
|
2724
|
+
break;
|
2725
|
+
}
|
2726
|
+
num += base * (((signed char *)buf)[i] + 1) * -1;
|
2727
|
+
base *= 128;
|
2728
|
+
}
|
2729
|
+
*sp = i + 1;
|
2730
|
+
return num;
|
2731
|
+
}
|
2732
|
+
|
2733
|
+
|
2734
|
+
|
2735
|
+
/* END OF FILE */
|