1 /*
2 * lws-api-test-fts - lws full-text search api test
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 */
9
10 #include <libwebsockets.h>
11 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
12 #include <getopt.h>
13 #endif
14 #include <fcntl.h>
15
16 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
17 static struct option options[] = {
18 { "help", no_argument, NULL, 'h' },
19 { "createindex", no_argument, NULL, 'c' },
20 { "index", required_argument, NULL, 'i' },
21 { "debug", required_argument, NULL, 'd' },
22 { "file", required_argument, NULL, 'f' },
23 { "lines", required_argument, NULL, 'l' },
24 { NULL, 0, 0, 0 }
25 };
26 #endif
27
28 static const char *index_filepath = "/tmp/lws-fts-test-index";
29 static char filepath[256];
30
main(int argc,char ** argv)31 int main(int argc, char **argv)
32 {
33 int n, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
34 int fd, fi, ft, createindex = 0, flags = LWSFTS_F_QUERY_AUTOCOMPLETE;
35 struct lws_fts_search_params params;
36 struct lws_fts_result *result;
37 struct lws_fts_file *jtf;
38 struct lws_fts *t;
39 char buf[16384];
40
41 do {
42 #if defined(LWS_HAS_GETOPT_LONG) || defined(WIN32)
43 n = getopt_long(argc, argv, "hd:i:cfl", options, NULL);
44 #else
45 n = getopt(argc, argv, "hd:i:cfl");
46 #endif
47 if (n < 0)
48 continue;
49 switch (n) {
50 case 'i':
51 strncpy(filepath, optarg, sizeof(filepath) - 1);
52 filepath[sizeof(filepath) - 1] = '\0';
53 index_filepath = filepath;
54 break;
55 case 'd':
56 logs = atoi(optarg);
57 break;
58 case 'c':
59 createindex = 1;
60 break;
61 case 'f':
62 flags &= ~LWSFTS_F_QUERY_AUTOCOMPLETE;
63 flags |= LWSFTS_F_QUERY_FILES;
64 break;
65 case 'l':
66 flags |= LWSFTS_F_QUERY_FILES |
67 LWSFTS_F_QUERY_FILE_LINES;
68 break;
69 case 'h':
70 fprintf(stderr,
71 "Usage: %s [--createindex]"
72 "[--index=<index filepath>] "
73 "[-d <log bitfield>] file1 file2 \n",
74 argv[0]);
75 exit(1);
76 }
77 } while (n >= 0);
78
79 lws_set_log_level(logs, NULL);
80 lwsl_user("LWS API selftest: full-text search\n");
81
82 if (createindex) {
83
84 lwsl_notice("Creating index\n");
85
86 /*
87 * create an index by shifting through argv and indexing each
88 * file given there into a single combined index
89 */
90
91 ft = open(index_filepath, O_CREAT | O_WRONLY | O_TRUNC, 0600);
92 if (ft < 0) {
93 lwsl_err("%s: can't open index %s\n", __func__,
94 index_filepath);
95
96 goto bail;
97 }
98
99 t = lws_fts_create(ft);
100 if (!t) {
101 lwsl_err("%s: Unable to allocate trie\n", __func__);
102
103 goto bail1;
104 }
105
106 while (optind < argc) {
107
108 fi = lws_fts_file_index(t, argv[optind],
109 (int)strlen(argv[optind]), 1);
110 if (fi < 0) {
111 lwsl_err("%s: Failed to get file idx for %s\n",
112 __func__, argv[optind]);
113
114 goto bail1;
115 }
116
117 fd = open(argv[optind], O_RDONLY);
118 if (fd < 0) {
119 lwsl_err("unable to open %s for read\n",
120 argv[optind]);
121 goto bail;
122 }
123
124 do {
125 int n = (int)read(fd, buf, sizeof(buf));
126
127 if (n <= 0)
128 break;
129
130 if (lws_fts_fill(t, (uint32_t)fi, buf, (size_t)n)) {
131 lwsl_err("%s: lws_fts_fill failed\n",
132 __func__);
133 close(fd);
134
135 goto bail;
136 }
137
138 } while (1);
139
140 close(fd);
141 optind++;
142 }
143
144 if (lws_fts_serialize(t)) {
145 lwsl_err("%s: serialize failed\n", __func__);
146
147 goto bail;
148 }
149
150 lws_fts_destroy(&t);
151 close(ft);
152
153 return 0;
154 }
155
156 /*
157 * shift through argv searching for each token
158 */
159
160 jtf = lws_fts_open(index_filepath);
161 if (!jtf)
162 goto bail;
163
164 while (optind < argc) {
165
166 struct lws_fts_result_autocomplete *ac;
167 struct lws_fts_result_filepath *fp;
168 uint32_t *l, n;
169
170 memset(¶ms, 0, sizeof(params));
171
172 params.needle = argv[optind];
173 params.flags = flags;
174 params.max_autocomplete = 20;
175 params.max_files = 20;
176
177 result = lws_fts_search(jtf, ¶ms);
178
179 if (!result) {
180 lwsl_err("%s: search failed\n", __func__);
181 lws_fts_close(jtf);
182 goto bail;
183 }
184
185 ac = result->autocomplete_head;
186 fp = result->filepath_head;
187
188 if (!ac)
189 lwsl_notice("%s: no autocomplete results\n", __func__);
190
191 while (ac) {
192 lwsl_notice("%s: AC %s: %d agg hits\n", __func__,
193 ((char *)(ac + 1)), ac->instances);
194
195 ac = ac->next;
196 }
197
198 if (!fp)
199 lwsl_notice("%s: no filepath results\n", __func__);
200
201 while (fp) {
202 lwsl_notice("%s: %s: (%d lines) %d hits \n", __func__,
203 (((char *)(fp + 1)) + fp->matches_length),
204 fp->lines_in_file, fp->matches);
205
206 if (fp->matches_length) {
207 l = (uint32_t *)(fp + 1);
208 n = 0;
209 while ((int)n++ < fp->matches)
210 lwsl_notice(" %d\n", *l++);
211 }
212 fp = fp->next;
213 }
214
215 lwsac_free(¶ms.results_head);
216
217 optind++;
218 }
219
220 lws_fts_close(jtf);
221
222 return 0;
223
224 bail1:
225 close(ft);
226 bail:
227 lwsl_user("FAILED\n");
228
229 return 1;
230 }
231