jk-ferret 0.11.8.2

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.
Files changed (228) hide show
  1. data/CHANGELOG +24 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +90 -0
  4. data/RELEASE_CHANGES +137 -0
  5. data/RELEASE_NOTES +60 -0
  6. data/Rakefile +443 -0
  7. data/TODO +109 -0
  8. data/TUTORIAL +231 -0
  9. data/bin/ferret-browser +79 -0
  10. data/ext/BZLIB_blocksort.c +1094 -0
  11. data/ext/BZLIB_bzlib.c +1578 -0
  12. data/ext/BZLIB_compress.c +672 -0
  13. data/ext/BZLIB_crctable.c +104 -0
  14. data/ext/BZLIB_decompress.c +626 -0
  15. data/ext/BZLIB_huffman.c +205 -0
  16. data/ext/BZLIB_randtable.c +84 -0
  17. data/ext/STEMMER_api.c +66 -0
  18. data/ext/STEMMER_libstemmer.c +93 -0
  19. data/ext/STEMMER_stem_ISO_8859_1_danish.c +337 -0
  20. data/ext/STEMMER_stem_ISO_8859_1_dutch.c +624 -0
  21. data/ext/STEMMER_stem_ISO_8859_1_english.c +1117 -0
  22. data/ext/STEMMER_stem_ISO_8859_1_finnish.c +762 -0
  23. data/ext/STEMMER_stem_ISO_8859_1_french.c +1246 -0
  24. data/ext/STEMMER_stem_ISO_8859_1_german.c +503 -0
  25. data/ext/STEMMER_stem_ISO_8859_1_hungarian.c +1230 -0
  26. data/ext/STEMMER_stem_ISO_8859_1_italian.c +1065 -0
  27. data/ext/STEMMER_stem_ISO_8859_1_norwegian.c +297 -0
  28. data/ext/STEMMER_stem_ISO_8859_1_porter.c +749 -0
  29. data/ext/STEMMER_stem_ISO_8859_1_portuguese.c +1017 -0
  30. data/ext/STEMMER_stem_ISO_8859_1_spanish.c +1093 -0
  31. data/ext/STEMMER_stem_ISO_8859_1_swedish.c +307 -0
  32. data/ext/STEMMER_stem_ISO_8859_2_romanian.c +998 -0
  33. data/ext/STEMMER_stem_KOI8_R_russian.c +700 -0
  34. data/ext/STEMMER_stem_UTF_8_danish.c +339 -0
  35. data/ext/STEMMER_stem_UTF_8_dutch.c +634 -0
  36. data/ext/STEMMER_stem_UTF_8_english.c +1125 -0
  37. data/ext/STEMMER_stem_UTF_8_finnish.c +768 -0
  38. data/ext/STEMMER_stem_UTF_8_french.c +1256 -0
  39. data/ext/STEMMER_stem_UTF_8_german.c +509 -0
  40. data/ext/STEMMER_stem_UTF_8_hungarian.c +1234 -0
  41. data/ext/STEMMER_stem_UTF_8_italian.c +1073 -0
  42. data/ext/STEMMER_stem_UTF_8_norwegian.c +299 -0
  43. data/ext/STEMMER_stem_UTF_8_porter.c +755 -0
  44. data/ext/STEMMER_stem_UTF_8_portuguese.c +1023 -0
  45. data/ext/STEMMER_stem_UTF_8_romanian.c +1004 -0
  46. data/ext/STEMMER_stem_UTF_8_russian.c +694 -0
  47. data/ext/STEMMER_stem_UTF_8_spanish.c +1097 -0
  48. data/ext/STEMMER_stem_UTF_8_swedish.c +309 -0
  49. data/ext/STEMMER_stem_UTF_8_turkish.c +2205 -0
  50. data/ext/STEMMER_utilities.c +478 -0
  51. data/ext/analysis.c +1710 -0
  52. data/ext/analysis.h +266 -0
  53. data/ext/api.h +26 -0
  54. data/ext/array.c +125 -0
  55. data/ext/array.h +62 -0
  56. data/ext/bitvector.c +96 -0
  57. data/ext/bitvector.h +594 -0
  58. data/ext/bzlib.h +282 -0
  59. data/ext/bzlib_private.h +503 -0
  60. data/ext/compound_io.c +384 -0
  61. data/ext/config.h +52 -0
  62. data/ext/document.c +159 -0
  63. data/ext/document.h +63 -0
  64. data/ext/except.c +102 -0
  65. data/ext/except.h +176 -0
  66. data/ext/extconf.rb +15 -0
  67. data/ext/ferret.c +416 -0
  68. data/ext/ferret.h +94 -0
  69. data/ext/field_index.c +262 -0
  70. data/ext/field_index.h +52 -0
  71. data/ext/filter.c +157 -0
  72. data/ext/fs_store.c +493 -0
  73. data/ext/global.c +458 -0
  74. data/ext/global.h +302 -0
  75. data/ext/hash.c +524 -0
  76. data/ext/hash.h +515 -0
  77. data/ext/hashset.c +192 -0
  78. data/ext/hashset.h +215 -0
  79. data/ext/header.h +58 -0
  80. data/ext/helper.c +63 -0
  81. data/ext/helper.h +21 -0
  82. data/ext/index.c +6804 -0
  83. data/ext/index.h +935 -0
  84. data/ext/internal.h +1019 -0
  85. data/ext/lang.c +10 -0
  86. data/ext/lang.h +68 -0
  87. data/ext/libstemmer.h +79 -0
  88. data/ext/mempool.c +88 -0
  89. data/ext/mempool.h +43 -0
  90. data/ext/modules.h +190 -0
  91. data/ext/multimapper.c +351 -0
  92. data/ext/multimapper.h +60 -0
  93. data/ext/posh.c +1006 -0
  94. data/ext/posh.h +973 -0
  95. data/ext/priorityqueue.c +149 -0
  96. data/ext/priorityqueue.h +155 -0
  97. data/ext/q_boolean.c +1621 -0
  98. data/ext/q_const_score.c +162 -0
  99. data/ext/q_filtered_query.c +212 -0
  100. data/ext/q_fuzzy.c +280 -0
  101. data/ext/q_match_all.c +149 -0
  102. data/ext/q_multi_term.c +673 -0
  103. data/ext/q_parser.c +3103 -0
  104. data/ext/q_phrase.c +1206 -0
  105. data/ext/q_prefix.c +98 -0
  106. data/ext/q_range.c +682 -0
  107. data/ext/q_span.c +2390 -0
  108. data/ext/q_term.c +337 -0
  109. data/ext/q_wildcard.c +167 -0
  110. data/ext/r_analysis.c +2626 -0
  111. data/ext/r_index.c +3468 -0
  112. data/ext/r_qparser.c +635 -0
  113. data/ext/r_search.c +4490 -0
  114. data/ext/r_store.c +513 -0
  115. data/ext/r_utils.c +1131 -0
  116. data/ext/ram_store.c +476 -0
  117. data/ext/scanner.c +895 -0
  118. data/ext/scanner.h +36 -0
  119. data/ext/scanner_mb.c +6701 -0
  120. data/ext/scanner_utf8.c +4415 -0
  121. data/ext/search.c +1864 -0
  122. data/ext/search.h +953 -0
  123. data/ext/similarity.c +151 -0
  124. data/ext/similarity.h +89 -0
  125. data/ext/sort.c +786 -0
  126. data/ext/stem_ISO_8859_1_danish.h +16 -0
  127. data/ext/stem_ISO_8859_1_dutch.h +16 -0
  128. data/ext/stem_ISO_8859_1_english.h +16 -0
  129. data/ext/stem_ISO_8859_1_finnish.h +16 -0
  130. data/ext/stem_ISO_8859_1_french.h +16 -0
  131. data/ext/stem_ISO_8859_1_german.h +16 -0
  132. data/ext/stem_ISO_8859_1_hungarian.h +16 -0
  133. data/ext/stem_ISO_8859_1_italian.h +16 -0
  134. data/ext/stem_ISO_8859_1_norwegian.h +16 -0
  135. data/ext/stem_ISO_8859_1_porter.h +16 -0
  136. data/ext/stem_ISO_8859_1_portuguese.h +16 -0
  137. data/ext/stem_ISO_8859_1_spanish.h +16 -0
  138. data/ext/stem_ISO_8859_1_swedish.h +16 -0
  139. data/ext/stem_ISO_8859_2_romanian.h +16 -0
  140. data/ext/stem_KOI8_R_russian.h +16 -0
  141. data/ext/stem_UTF_8_danish.h +16 -0
  142. data/ext/stem_UTF_8_dutch.h +16 -0
  143. data/ext/stem_UTF_8_english.h +16 -0
  144. data/ext/stem_UTF_8_finnish.h +16 -0
  145. data/ext/stem_UTF_8_french.h +16 -0
  146. data/ext/stem_UTF_8_german.h +16 -0
  147. data/ext/stem_UTF_8_hungarian.h +16 -0
  148. data/ext/stem_UTF_8_italian.h +16 -0
  149. data/ext/stem_UTF_8_norwegian.h +16 -0
  150. data/ext/stem_UTF_8_porter.h +16 -0
  151. data/ext/stem_UTF_8_portuguese.h +16 -0
  152. data/ext/stem_UTF_8_romanian.h +16 -0
  153. data/ext/stem_UTF_8_russian.h +16 -0
  154. data/ext/stem_UTF_8_spanish.h +16 -0
  155. data/ext/stem_UTF_8_swedish.h +16 -0
  156. data/ext/stem_UTF_8_turkish.h +16 -0
  157. data/ext/stopwords.c +410 -0
  158. data/ext/store.c +698 -0
  159. data/ext/store.h +799 -0
  160. data/ext/symbol.c +10 -0
  161. data/ext/symbol.h +23 -0
  162. data/ext/term_vectors.c +73 -0
  163. data/ext/threading.h +31 -0
  164. data/ext/win32.h +62 -0
  165. data/lib/ferret.rb +30 -0
  166. data/lib/ferret/browser.rb +246 -0
  167. data/lib/ferret/browser/s/global.js +192 -0
  168. data/lib/ferret/browser/s/style.css +148 -0
  169. data/lib/ferret/browser/views/document/list.rhtml +49 -0
  170. data/lib/ferret/browser/views/document/show.rhtml +27 -0
  171. data/lib/ferret/browser/views/error/index.rhtml +7 -0
  172. data/lib/ferret/browser/views/help/index.rhtml +8 -0
  173. data/lib/ferret/browser/views/home/index.rhtml +29 -0
  174. data/lib/ferret/browser/views/layout.rhtml +22 -0
  175. data/lib/ferret/browser/views/term-vector/index.rhtml +4 -0
  176. data/lib/ferret/browser/views/term/index.rhtml +199 -0
  177. data/lib/ferret/browser/views/term/termdocs.rhtml +1 -0
  178. data/lib/ferret/browser/webrick.rb +14 -0
  179. data/lib/ferret/document.rb +130 -0
  180. data/lib/ferret/field_infos.rb +44 -0
  181. data/lib/ferret/field_symbol.rb +87 -0
  182. data/lib/ferret/index.rb +973 -0
  183. data/lib/ferret/number_tools.rb +157 -0
  184. data/lib/ferret/version.rb +3 -0
  185. data/setup.rb +1555 -0
  186. data/test/long_running/largefile/tc_largefile.rb +46 -0
  187. data/test/test_all.rb +5 -0
  188. data/test/test_helper.rb +29 -0
  189. data/test/test_installed.rb +1 -0
  190. data/test/threading/number_to_spoken.rb +132 -0
  191. data/test/threading/thread_safety_index_test.rb +88 -0
  192. data/test/threading/thread_safety_read_write_test.rb +73 -0
  193. data/test/threading/thread_safety_test.rb +133 -0
  194. data/test/unit/analysis/tc_analyzer.rb +550 -0
  195. data/test/unit/analysis/tc_token_stream.rb +653 -0
  196. data/test/unit/index/tc_index.rb +867 -0
  197. data/test/unit/index/tc_index_reader.rb +699 -0
  198. data/test/unit/index/tc_index_writer.rb +447 -0
  199. data/test/unit/index/th_doc.rb +332 -0
  200. data/test/unit/query_parser/tc_query_parser.rb +238 -0
  201. data/test/unit/search/tc_filter.rb +156 -0
  202. data/test/unit/search/tc_fuzzy_query.rb +147 -0
  203. data/test/unit/search/tc_index_searcher.rb +67 -0
  204. data/test/unit/search/tc_multi_searcher.rb +128 -0
  205. data/test/unit/search/tc_multiple_search_requests.rb +58 -0
  206. data/test/unit/search/tc_search_and_sort.rb +179 -0
  207. data/test/unit/search/tc_sort.rb +49 -0
  208. data/test/unit/search/tc_sort_field.rb +27 -0
  209. data/test/unit/search/tc_spans.rb +190 -0
  210. data/test/unit/search/tm_searcher.rb +436 -0
  211. data/test/unit/store/tc_fs_store.rb +115 -0
  212. data/test/unit/store/tc_ram_store.rb +35 -0
  213. data/test/unit/store/tm_store.rb +34 -0
  214. data/test/unit/store/tm_store_lock.rb +68 -0
  215. data/test/unit/tc_document.rb +81 -0
  216. data/test/unit/tc_field_symbol.rb +26 -0
  217. data/test/unit/ts_analysis.rb +2 -0
  218. data/test/unit/ts_index.rb +2 -0
  219. data/test/unit/ts_largefile.rb +4 -0
  220. data/test/unit/ts_query_parser.rb +2 -0
  221. data/test/unit/ts_search.rb +2 -0
  222. data/test/unit/ts_store.rb +2 -0
  223. data/test/unit/ts_utils.rb +2 -0
  224. data/test/unit/utils/tc_bit_vector.rb +295 -0
  225. data/test/unit/utils/tc_number_tools.rb +117 -0
  226. data/test/unit/utils/tc_priority_queue.rb +106 -0
  227. data/test/utils/content_generator.rb +226 -0
  228. metadata +319 -0
data/ext/fs_store.c ADDED
@@ -0,0 +1,493 @@
1
+ #include "store.h"
2
+ #include "lang.h"
3
+ #include <time.h>
4
+ #include <sys/types.h>
5
+ #include <fcntl.h>
6
+ #include <sys/stat.h>
7
+ #include <errno.h>
8
+ #include <string.h>
9
+ #include <stdio.h>
10
+ #ifdef POSH_OS_WIN32
11
+ # include <io.h>
12
+ # include "win32.h"
13
+ # ifndef sleep
14
+ # define sleep _sleep
15
+ # endif
16
+ # ifndef DIR_SEPARATOR
17
+ # define DIR_SEPARATOR "\\"
18
+ # define DIR_SEPARATOR_CHAR '\\'
19
+ # endif
20
+ # ifndef S_IRUSR
21
+ # define S_IRUSR _S_IREAD
22
+ # endif
23
+ # ifndef S_IWUSR
24
+ # define S_IWUSR _S_IWRITE
25
+ # endif
26
+ #else
27
+ # define DIR_SEPARATOR "/"
28
+ # define DIR_SEPARATOR_CHAR '/'
29
+ # include <unistd.h>
30
+ # include <dirent.h>
31
+ #endif
32
+ #ifndef O_BINARY
33
+ # define O_BINARY 0
34
+ #endif
35
+ #include "internal.h"
36
+
37
+ /**
38
+ * Create a filepath for a file in the store using the operating systems
39
+ * default file seperator.
40
+ */
41
+ static char *join_path(char *buf, const char *base, const char *filename)
42
+ {
43
+ snprintf(buf, MAX_FILE_PATH, "%s"DIR_SEPARATOR"%s", base, filename);
44
+ return buf;
45
+ }
46
+
47
+ static void fs_touch(Store *store, const char *filename)
48
+ {
49
+ int f;
50
+ char path[MAX_FILE_PATH];
51
+ join_path(path, store->dir.path, filename);
52
+ if ((f = creat(path, store->file_mode)) == 0) {
53
+ RAISE(IO_ERROR, "couldn't create file %s: <%s>", path,
54
+ strerror(errno));
55
+ }
56
+ close(f);
57
+ }
58
+
59
+ static int fs_exists(Store *store, const char *filename)
60
+ {
61
+ int fd;
62
+ char path[MAX_FILE_PATH];
63
+ join_path(path, store->dir.path, filename);
64
+ fd = open(path, 0);
65
+ if (fd < 0) {
66
+ if (errno != ENOENT) {
67
+ RAISE(IO_ERROR, "checking existance of %s: <%s>", path,
68
+ strerror(errno));
69
+ }
70
+ return false;
71
+ }
72
+ close(fd);
73
+ return true;
74
+ }
75
+
76
+ static int fs_remove(Store *store, const char *filename)
77
+ {
78
+ char path[MAX_FILE_PATH];
79
+ return remove(join_path(path, store->dir.path, filename));
80
+ }
81
+
82
+ static void fs_rename(Store *store, const char *from, const char *to)
83
+ {
84
+ char path1[MAX_FILE_PATH], path2[MAX_FILE_PATH];
85
+
86
+ #ifdef POSH_OS_WIN32
87
+ remove(join_path(path1, store->dir.path, to));
88
+ #endif
89
+
90
+ if (rename(join_path(path1, store->dir.path, from),
91
+ join_path(path2, store->dir.path, to)) < 0) {
92
+ RAISE(IO_ERROR, "couldn't rename file \"%s\" to \"%s\": <%s>",
93
+ path1, path2, strerror(errno));
94
+ }
95
+ }
96
+
97
+ static int fs_count(Store *store)
98
+ {
99
+ int cnt = 0;
100
+ struct dirent *de;
101
+ DIR *d = opendir(store->dir.path);
102
+
103
+ if (!d) {
104
+ RAISE(IO_ERROR, "counting files in %s: <%s>",
105
+ store->dir.path, strerror(errno));
106
+ }
107
+
108
+ while ((de = readdir(d)) != NULL) {
109
+ if (de->d_name[0] > '/') { /* skip ., .., / and '\0'*/
110
+ cnt++;
111
+ }
112
+ }
113
+ closedir(d);
114
+
115
+ return cnt;
116
+ }
117
+
118
+ static void fs_each(Store *store, void (*func)(const char *fname, void *arg), void *arg)
119
+ {
120
+ struct dirent *de;
121
+ DIR *d = opendir(store->dir.path);
122
+
123
+ if (!d) {
124
+ RAISE(IO_ERROR, "doing 'each' in %s: <%s>",
125
+ store->dir.path, strerror(errno));
126
+ }
127
+
128
+ while ((de = readdir(d)) != NULL) {
129
+ if (de->d_name[0] > '/' /* skip ., .., / and '\0'*/
130
+ && !file_is_lock(de->d_name)) {
131
+ func(de->d_name, arg);
132
+ }
133
+ }
134
+ closedir(d);
135
+ }
136
+
137
+ static void fs_clear_locks(Store *store)
138
+ {
139
+ struct dirent *de;
140
+ DIR *d = opendir(store->dir.path);
141
+
142
+ if (!d) {
143
+ RAISE(IO_ERROR, "clearing locks in %s: <%s>",
144
+ store->dir.path, strerror(errno));
145
+ }
146
+
147
+ while ((de = readdir(d)) != NULL) {
148
+ if (file_is_lock(de->d_name)) {
149
+ char path[MAX_FILE_PATH];
150
+ remove(join_path(path, store->dir.path, de->d_name));
151
+ }
152
+ }
153
+ closedir(d);
154
+ }
155
+
156
+ static void fs_clear(Store *store)
157
+ {
158
+ struct dirent *de;
159
+ DIR *d = opendir(store->dir.path);
160
+
161
+ if (!d) {
162
+ RAISE(IO_ERROR, "clearing files in %s: <%s>",
163
+ store->dir.path, strerror(errno));
164
+ }
165
+
166
+ while ((de = readdir(d)) != NULL) {
167
+ if (de->d_name[0] > '/' /* skip ., .., / and '\0'*/
168
+ && !file_is_lock(de->d_name)) {
169
+ char path[MAX_FILE_PATH];
170
+ remove(join_path(path, store->dir.path, de->d_name));
171
+ }
172
+ }
173
+ closedir(d);
174
+ }
175
+
176
+ static void fs_clear_all(Store *store)
177
+ {
178
+ struct dirent *de;
179
+ DIR *d = opendir(store->dir.path);
180
+
181
+ if (!d) {
182
+ RAISE(IO_ERROR, "clearing all files in %s: <%s>",
183
+ store->dir.path, strerror(errno));
184
+ }
185
+
186
+ while ((de = readdir(d)) != NULL) {
187
+ if (de->d_name[0] > '/') { /* skip ., .., / and '\0'*/
188
+ char path[MAX_FILE_PATH];
189
+ char *basename;
190
+ join_path(path, store->dir.path, de->d_name);
191
+ /* get basename of path */
192
+ basename = strrchr(path, DIR_SEPARATOR_CHAR);
193
+ basename = (basename ? basename + 1 : path);
194
+ /* we don't want to delete no index files here */
195
+ if (file_name_filter_is_index_file(basename, true)) {
196
+ remove(path);
197
+ }
198
+ }
199
+ }
200
+ closedir(d);
201
+ }
202
+
203
+ /**
204
+ * Destroy the store.
205
+ *
206
+ * @param p the store to destroy
207
+ * @raise IO_ERROR if there is an error deleting the locks
208
+ */
209
+ static void fs_destroy(Store *store)
210
+ {
211
+ TRY
212
+ fs_clear_locks(store);
213
+ XCATCHALL
214
+ HANDLED();
215
+ XENDTRY
216
+ free(store->dir.path);
217
+ store_destroy(store);
218
+ }
219
+
220
+ static off_t fs_length(Store *store, const char *filename)
221
+ {
222
+ char path[MAX_FILE_PATH];
223
+ struct stat stt;
224
+
225
+ if (stat(join_path(path, store->dir.path, filename), &stt)) {
226
+ RAISE(IO_ERROR, "getting lenth of %s: <%s>", path,
227
+ strerror(errno));
228
+ }
229
+
230
+ return stt.st_size;
231
+ }
232
+
233
+ static void fso_flush_i(OutStream *os, const uchar *src, int len)
234
+ {
235
+ if (len != write(os->file.fd, src, len)) {
236
+ RAISE(IO_ERROR, "flushing src of length %d, <%s>", len,
237
+ strerror(errno));
238
+ }
239
+ }
240
+
241
+ static void fso_seek_i(OutStream *os, off_t pos)
242
+ {
243
+ if (lseek(os->file.fd, pos, SEEK_SET) < 0) {
244
+ RAISE(IO_ERROR, "seeking position %"OFF_T_PFX"d: <%s>",
245
+ pos, strerror(errno));
246
+ }
247
+ }
248
+
249
+ static void fso_close_i(OutStream *os)
250
+ {
251
+ if (close(os->file.fd)) {
252
+ RAISE(IO_ERROR, "closing file: <%s>", strerror(errno));
253
+ }
254
+ }
255
+
256
+ static const struct OutStreamMethods FS_OUT_STREAM_METHODS = {
257
+ fso_flush_i,
258
+ fso_seek_i,
259
+ fso_close_i
260
+ };
261
+
262
+ static OutStream *fs_new_output(Store *store, const char *filename)
263
+ {
264
+ char path[MAX_FILE_PATH];
265
+ int fd = open(join_path(path, store->dir.path, filename),
266
+ O_WRONLY | O_CREAT | O_BINARY, store->file_mode);
267
+ OutStream *os;
268
+ if (fd < 0) {
269
+ RAISE(IO_ERROR, "couldn't create OutStream %s: <%s>",
270
+ path, strerror(errno));
271
+ }
272
+
273
+ os = os_new();
274
+ os->file.fd = fd;
275
+ os->m = &FS_OUT_STREAM_METHODS;
276
+ return os;
277
+ }
278
+
279
+ static void fsi_read_i(InStream *is, uchar *path, int len)
280
+ {
281
+ int fd = is->file.fd;
282
+ off_t pos = is_pos(is);
283
+ if (pos != lseek(fd, 0, SEEK_CUR)) {
284
+ lseek(fd, pos, SEEK_SET);
285
+ }
286
+ if (read(fd, path, len) != len) {
287
+ /* win: the wrong value can be returned for some reason so double check */
288
+ if (lseek(fd, 0, SEEK_CUR) != (pos + len)) {
289
+ RAISE(IO_ERROR, "couldn't read %d chars from %s: <%s>",
290
+ len, path, strerror(errno));
291
+ }
292
+ }
293
+ }
294
+
295
+ static void fsi_seek_i(InStream *is, off_t pos)
296
+ {
297
+ if (lseek(is->file.fd, pos, SEEK_SET) < 0) {
298
+ RAISE(IO_ERROR, "seeking pos %"OFF_T_PFX"d: <%s>",
299
+ pos, strerror(errno));
300
+ }
301
+ }
302
+
303
+ static void fsi_close_i(InStream *is)
304
+ {
305
+ if (close(is->file.fd)) {
306
+ RAISE(IO_ERROR, "%s", strerror(errno));
307
+ }
308
+ free(is->d.path);
309
+ }
310
+
311
+ static off_t fsi_length_i(InStream *is)
312
+ {
313
+ struct stat stt;
314
+ if (fstat(is->file.fd, &stt)) {
315
+ RAISE(IO_ERROR, "fstat failed: <%s>", strerror(errno));
316
+ }
317
+ return stt.st_size;
318
+ }
319
+
320
+ static const struct InStreamMethods FS_IN_STREAM_METHODS = {
321
+ fsi_read_i,
322
+ fsi_seek_i,
323
+ fsi_length_i,
324
+ fsi_close_i
325
+ };
326
+
327
+ static InStream *fs_open_input(Store *store, const char *filename)
328
+ {
329
+ InStream *is;
330
+ char path[MAX_FILE_PATH];
331
+ int fd = open(join_path(path, store->dir.path, filename), O_RDONLY | O_BINARY);
332
+ if (fd < 0) {
333
+ RAISE(FILE_NOT_FOUND_ERROR,
334
+ "tried to open \"%s\" but it doesn't exist: <%s>",
335
+ path, strerror(errno));
336
+ }
337
+ is = is_new();
338
+ is->file.fd = fd;
339
+ is->d.path = estrdup(path);
340
+ is->m = &FS_IN_STREAM_METHODS;
341
+ return is;
342
+ }
343
+
344
+ #define LOCK_OBTAIN_TIMEOUT 10
345
+
346
+ static int fs_lock_obtain(Lock *lock)
347
+ {
348
+ int f;
349
+ int trys = LOCK_OBTAIN_TIMEOUT;
350
+ while (((f =
351
+ open(lock->name, O_CREAT | O_EXCL | O_RDWR,
352
+ S_IRUSR | S_IWUSR)) < 0) && (trys > 0)) {
353
+
354
+ /* sleep for 10 milliseconds */
355
+ micro_sleep(10000);
356
+ trys--;
357
+ }
358
+ if (f >= 0) {
359
+ close(f);
360
+ return true;
361
+ }
362
+ else {
363
+ return false;
364
+ }
365
+ }
366
+
367
+ static int fs_lock_is_locked(Lock *lock)
368
+ {
369
+ int f = open(lock->name, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
370
+ if (f >= 0) {
371
+ if (close(f) || remove(lock->name)) {
372
+ RAISE(IO_ERROR, "couldn't close lock \"%s\": <%s>", lock->name,
373
+ strerror(errno));
374
+ }
375
+ return false;
376
+ }
377
+ else {
378
+ return true;
379
+ }
380
+ }
381
+
382
+ static void fs_lock_release(Lock *lock)
383
+ {
384
+ remove(lock->name);
385
+ }
386
+
387
+ static Lock *fs_open_lock_i(Store *store, const char *lockname)
388
+ {
389
+ Lock *lock = ALLOC(Lock);
390
+ char lname[100];
391
+ char path[MAX_FILE_PATH];
392
+ snprintf(lname, 100, "%s%s.lck", LOCK_PREFIX, lockname);
393
+ lock->name = estrdup(join_path(path, store->dir.path, lname));
394
+ lock->store = store;
395
+ lock->obtain = &fs_lock_obtain;
396
+ lock->release = &fs_lock_release;
397
+ lock->is_locked = &fs_lock_is_locked;
398
+ return lock;
399
+ }
400
+
401
+ static void fs_close_lock_i(Lock *lock)
402
+ {
403
+ remove(lock->name);
404
+ free(lock->name);
405
+ free(lock);
406
+ }
407
+
408
+ static Hash *stores = NULL;
409
+
410
+ #ifndef UNTHREADED
411
+ static mutex_t stores_mutex = MUTEX_INITIALIZER;
412
+ #endif
413
+
414
+ static void fs_close_i(Store *store)
415
+ {
416
+ mutex_lock(&stores_mutex);
417
+ h_del(stores, store->dir.path);
418
+ mutex_unlock(&stores_mutex);
419
+ }
420
+
421
+ static Store *fs_store_new(const char *pathname)
422
+ {
423
+ struct stat stt;
424
+ Store *new_store = store_new();
425
+
426
+ new_store->file_mode = S_IRUSR | S_IWUSR;
427
+ #ifndef POSH_OS_WIN32
428
+ if (!stat(pathname, &stt)) {
429
+ gid_t st_gid = stt.st_gid;
430
+ bool is_grp = (st_gid == getgid());
431
+
432
+ if (!is_grp) {
433
+ int size = getgroups(0, NULL);
434
+ gid_t list[size];
435
+
436
+ if (getgroups(size, list) != -1) {
437
+ int i = 0;
438
+ while (i < size && !(is_grp = (st_gid == list[i]))) i++;
439
+ }
440
+ }
441
+
442
+ if (is_grp) {
443
+ if (stt.st_mode & S_IWGRP) {
444
+ umask(S_IWOTH);
445
+ }
446
+ new_store->file_mode |= stt.st_mode & (S_IRGRP | S_IWGRP);
447
+ }
448
+ }
449
+ #endif
450
+
451
+ new_store->dir.path = estrdup(pathname);
452
+ new_store->touch = &fs_touch;
453
+ new_store->exists = &fs_exists;
454
+ new_store->remove = &fs_remove;
455
+ new_store->rename = &fs_rename;
456
+ new_store->count = &fs_count;
457
+ new_store->close_i = &fs_close_i;
458
+ new_store->clear = &fs_clear;
459
+ new_store->clear_all = &fs_clear_all;
460
+ new_store->clear_locks = &fs_clear_locks;
461
+ new_store->length = &fs_length;
462
+ new_store->each = &fs_each;
463
+ new_store->new_output = &fs_new_output;
464
+ new_store->open_input = &fs_open_input;
465
+ new_store->open_lock_i = &fs_open_lock_i;
466
+ new_store->close_lock_i = &fs_close_lock_i;
467
+ return new_store;
468
+ }
469
+
470
+ Store *open_fs_store(const char *pathname)
471
+ {
472
+ Store *store = NULL;
473
+
474
+ if (!stores) {
475
+ stores = h_new_str(NULL, (free_ft)fs_destroy);
476
+ register_for_cleanup(stores, (free_ft)h_destroy);
477
+ }
478
+
479
+ mutex_lock(&stores_mutex);
480
+ store = (Store *)h_get(stores, pathname);
481
+ if (store) {
482
+ mutex_lock(&store->mutex);
483
+ store->ref_cnt++;
484
+ mutex_unlock(&store->mutex);
485
+ }
486
+ else {
487
+ store = fs_store_new(pathname);
488
+ h_set(stores, store->dir.path, store);
489
+ }
490
+ mutex_unlock(&stores_mutex);
491
+
492
+ return store;
493
+ }