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) 17static 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 28static const char *index_filepath = "/tmp/lws-fts-test-index"; 29static char filepath[256]; 30 31int 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 224bail1: 225 close(ft); 226bail: 227 lwsl_user("FAILED\n"); 228 229 return 1; 230} 231