| @@ -1,2060 +1,2054 @@ | | | @@ -1,2060 +1,2054 @@ |
1 | /* $NetBSD: makemandb.c,v 1.26 2015/03/02 13:51:24 joerg Exp $ */ | | 1 | /* $NetBSD: makemandb.c,v 1.27 2015/03/04 02:02:15 christos Exp $ */ |
2 | /* | | 2 | /* |
3 | * Copyright (c) 2011 Abhinav Upadhyay <er.abhinav.upadhyay@gmail.com> | | 3 | * Copyright (c) 2011 Abhinav Upadhyay <er.abhinav.upadhyay@gmail.com> |
4 | * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> | | 4 | * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
5 | * | | 5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any | | 6 | * Permission to use, copy, modify, and distribute this software for any |
7 | * purpose with or without fee is hereby granted, provided that the above | | 7 | * purpose with or without fee is hereby granted, provided that the above |
8 | * copyright notice and this permission notice appear in all copies. | | 8 | * copyright notice and this permission notice appear in all copies. |
9 | * | | 9 | * |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | | 17 | */ |
18 | | | 18 | |
19 | #include <sys/cdefs.h> | | 19 | #include <sys/cdefs.h> |
20 | __RCSID("$NetBSD: makemandb.c,v 1.26 2015/03/02 13:51:24 joerg Exp $"); | | 20 | __RCSID("$NetBSD: makemandb.c,v 1.27 2015/03/04 02:02:15 christos Exp $"); |
21 | | | 21 | |
22 | #include <sys/stat.h> | | 22 | #include <sys/stat.h> |
23 | #include <sys/types.h> | | 23 | #include <sys/types.h> |
24 | | | 24 | |
25 | #include <assert.h> | | 25 | #include <assert.h> |
26 | #include <ctype.h> | | 26 | #include <ctype.h> |
27 | #include <dirent.h> | | 27 | #include <dirent.h> |
28 | #include <err.h> | | 28 | #include <err.h> |
29 | #include <archive.h> | | 29 | #include <archive.h> |
30 | #include <libgen.h> | | 30 | #include <libgen.h> |
31 | #include <md5.h> | | 31 | #include <md5.h> |
32 | #include <stdio.h> | | 32 | #include <stdio.h> |
33 | #include <stdlib.h> | | 33 | #include <stdlib.h> |
34 | #include <string.h> | | 34 | #include <string.h> |
35 | #include <unistd.h> | | 35 | #include <unistd.h> |
36 | #include <util.h> | | 36 | #include <util.h> |
37 | | | 37 | |
38 | #include "apropos-utils.h" | | 38 | #include "apropos-utils.h" |
39 | #include "dist/man.h" | | 39 | #include "dist/man.h" |
40 | #include "dist/mandoc.h" | | 40 | #include "dist/mandoc.h" |
41 | #include "dist/mdoc.h" | | 41 | #include "dist/mdoc.h" |
42 | #include "sqlite3.h" | | 42 | #include "sqlite3.h" |
43 | | | 43 | |
44 | #define BUFLEN 1024 | | 44 | #define BUFLEN 1024 |
45 | #define MDOC 0 //If the page is of mdoc(7) type | | 45 | #define MDOC 0 //If the page is of mdoc(7) type |
46 | #define MAN 1 //If the page is of man(7) type | | 46 | #define MAN 1 //If the page is of man(7) type |
47 | | | 47 | |
48 | /* | | 48 | /* |
49 | * A data structure for holding section specific data. | | 49 | * A data structure for holding section specific data. |
50 | */ | | 50 | */ |
51 | typedef struct secbuff { | | 51 | typedef struct secbuff { |
52 | char *data; | | 52 | char *data; |
53 | size_t buflen; //Total length of buffer allocated initially | | 53 | size_t buflen; //Total length of buffer allocated initially |
54 | size_t offset; // Current offset in the buffer. | | 54 | size_t offset; // Current offset in the buffer. |
55 | } secbuff; | | 55 | } secbuff; |
56 | | | 56 | |
57 | typedef struct makemandb_flags { | | 57 | typedef struct makemandb_flags { |
58 | int optimize; | | 58 | int optimize; |
59 | int limit; // limit the indexing to only NAME section | | 59 | int limit; // limit the indexing to only NAME section |
60 | int recreate; // Database was created from scratch | | 60 | int recreate; // Database was created from scratch |
61 | int verbosity; // 0: quiet, 1: default, 2: verbose | | 61 | int verbosity; // 0: quiet, 1: default, 2: verbose |
62 | } makemandb_flags; | | 62 | } makemandb_flags; |
63 | | | 63 | |
64 | typedef struct mandb_rec { | | 64 | typedef struct mandb_rec { |
65 | /* Fields for mandb table */ | | 65 | /* Fields for mandb table */ |
66 | char *name; // for storing the name of the man page | | 66 | char *name; // for storing the name of the man page |
67 | char *name_desc; // for storing the one line description (.Nd) | | 67 | char *name_desc; // for storing the one line description (.Nd) |
68 | secbuff desc; // for storing the DESCRIPTION section | | 68 | secbuff desc; // for storing the DESCRIPTION section |
69 | secbuff lib; // for the LIBRARY section | | 69 | secbuff lib; // for the LIBRARY section |
70 | secbuff return_vals; // RETURN VALUES | | 70 | secbuff return_vals; // RETURN VALUES |
71 | secbuff env; // ENVIRONMENT | | 71 | secbuff env; // ENVIRONMENT |
72 | secbuff files; // FILES | | 72 | secbuff files; // FILES |
73 | secbuff exit_status; // EXIT STATUS | | 73 | secbuff exit_status; // EXIT STATUS |
74 | secbuff diagnostics; // DIAGNOSTICS | | 74 | secbuff diagnostics; // DIAGNOSTICS |
75 | secbuff errors; // ERRORS | | 75 | secbuff errors; // ERRORS |
76 | char section[2]; | | 76 | char section[2]; |
77 | | | 77 | |
78 | int xr_found; | | 78 | int xr_found; |
79 | | | 79 | |
80 | /* Fields for mandb_meta table */ | | 80 | /* Fields for mandb_meta table */ |
81 | char *md5_hash; | | 81 | char *md5_hash; |
82 | dev_t device; | | 82 | dev_t device; |
83 | ino_t inode; | | 83 | ino_t inode; |
84 | time_t mtime; | | 84 | time_t mtime; |
85 | | | 85 | |
86 | /* Fields for mandb_links table */ | | 86 | /* Fields for mandb_links table */ |
87 | char *machine; | | 87 | char *machine; |
88 | char *links; //all the links to a page in a space separated form | | 88 | char *links; //all the links to a page in a space separated form |
89 | char *file_path; | | 89 | char *file_path; |
90 | | | 90 | |
91 | /* Non-db fields */ | | 91 | /* Non-db fields */ |
92 | int page_type; //Indicates the type of page: mdoc or man | | 92 | int page_type; //Indicates the type of page: mdoc or man |
93 | } mandb_rec; | | 93 | } mandb_rec; |
94 | | | 94 | |
95 | static void append(secbuff *sbuff, const char *src); | | 95 | static void append(secbuff *sbuff, const char *src); |
96 | static void init_secbuffs(mandb_rec *); | | 96 | static void init_secbuffs(mandb_rec *); |
97 | static void free_secbuffs(mandb_rec *); | | 97 | static void free_secbuffs(mandb_rec *); |
98 | static int check_md5(const char *, sqlite3 *, const char *, char **, void *, size_t); | | 98 | static int check_md5(const char *, sqlite3 *, const char *, char **, void *, size_t); |
99 | static void cleanup(mandb_rec *); | | 99 | static void cleanup(mandb_rec *); |
100 | static void set_section(const struct mdoc *, const struct man *, mandb_rec *); | | 100 | static void set_section(const struct mdoc *, const struct man *, mandb_rec *); |
101 | static void set_machine(const struct mdoc *, mandb_rec *); | | 101 | static void set_machine(const struct mdoc *, mandb_rec *); |
102 | static int insert_into_db(sqlite3 *, mandb_rec *); | | 102 | static int insert_into_db(sqlite3 *, mandb_rec *); |
103 | static void begin_parse(const char *, struct mparse *, mandb_rec *, | | 103 | static void begin_parse(const char *, struct mparse *, mandb_rec *, |
104 | const void *, size_t len); | | 104 | const void *, size_t len); |
105 | static void pmdoc_node(const struct mdoc_node *, mandb_rec *); | | 105 | static void pmdoc_node(const struct mdoc_node *, mandb_rec *); |
106 | static void pmdoc_Nm(const struct mdoc_node *, mandb_rec *); | | 106 | static void pmdoc_Nm(const struct mdoc_node *, mandb_rec *); |
107 | static void pmdoc_Nd(const struct mdoc_node *, mandb_rec *); | | 107 | static void pmdoc_Nd(const struct mdoc_node *, mandb_rec *); |
108 | static void pmdoc_Sh(const struct mdoc_node *, mandb_rec *); | | 108 | static void pmdoc_Sh(const struct mdoc_node *, mandb_rec *); |
109 | static void pmdoc_Xr(const struct mdoc_node *, mandb_rec *); | | 109 | static void pmdoc_Xr(const struct mdoc_node *, mandb_rec *); |
110 | static void pmdoc_Pp(const struct mdoc_node *, mandb_rec *); | | 110 | static void pmdoc_Pp(const struct mdoc_node *, mandb_rec *); |
111 | static void pmdoc_macro_handler(const struct mdoc_node *, mandb_rec *, | | 111 | static void pmdoc_macro_handler(const struct mdoc_node *, mandb_rec *, |
112 | enum mdoct); | | 112 | enum mdoct); |
113 | static void pman_node(const struct man_node *n, mandb_rec *); | | 113 | static void pman_node(const struct man_node *n, mandb_rec *); |
114 | static void pman_parse_node(const struct man_node *, secbuff *); | | 114 | static void pman_parse_node(const struct man_node *, secbuff *); |
115 | static void pman_parse_name(const struct man_node *, mandb_rec *); | | 115 | static void pman_parse_name(const struct man_node *, mandb_rec *); |
116 | static void pman_sh(const struct man_node *, mandb_rec *); | | 116 | static void pman_sh(const struct man_node *, mandb_rec *); |
117 | static void pman_block(const struct man_node *, mandb_rec *); | | 117 | static void pman_block(const struct man_node *, mandb_rec *); |
118 | static void traversedir(const char *, const char *, sqlite3 *, struct mparse *); | | 118 | static void traversedir(const char *, const char *, sqlite3 *, struct mparse *); |
119 | static void mdoc_parse_section(enum mdoc_sec, const char *, mandb_rec *); | | 119 | static void mdoc_parse_section(enum mdoc_sec, const char *, mandb_rec *); |
120 | static void man_parse_section(enum man_sec, const struct man_node *, mandb_rec *); | | 120 | static void man_parse_section(enum man_sec, const struct man_node *, mandb_rec *); |
121 | static void build_file_cache(sqlite3 *, const char *, const char *, | | 121 | static void build_file_cache(sqlite3 *, const char *, const char *, |
122 | struct stat *); | | 122 | struct stat *); |
123 | static void update_db(sqlite3 *, struct mparse *, mandb_rec *); | | 123 | static void update_db(sqlite3 *, struct mparse *, mandb_rec *); |
124 | __dead static void usage(void); | | 124 | __dead static void usage(void); |
125 | static void optimize(sqlite3 *); | | 125 | static void optimize(sqlite3 *); |
126 | static char *parse_escape(const char *); | | 126 | static char *parse_escape(const char *); |
127 | static void replace_hyph(char *); | | 127 | static void replace_hyph(char *); |
128 | static makemandb_flags mflags = { .verbosity = 1 }; | | 128 | static makemandb_flags mflags = { .verbosity = 1 }; |
129 | | | 129 | |
130 | typedef void (*pman_nf)(const struct man_node *n, mandb_rec *); | | 130 | typedef void (*pman_nf)(const struct man_node *n, mandb_rec *); |
131 | typedef void (*pmdoc_nf)(const struct mdoc_node *n, mandb_rec *); | | 131 | typedef void (*pmdoc_nf)(const struct mdoc_node *n, mandb_rec *); |
132 | static const pmdoc_nf mdocs[MDOC_MAX] = { | | 132 | static const pmdoc_nf mdocs[MDOC_MAX] = { |
133 | NULL, /* Ap */ | | 133 | NULL, /* Ap */ |
134 | NULL, /* Dd */ | | 134 | NULL, /* Dd */ |
135 | NULL, /* Dt */ | | 135 | NULL, /* Dt */ |
136 | NULL, /* Os */ | | 136 | NULL, /* Os */ |
137 | pmdoc_Sh, /* Sh */ | | 137 | pmdoc_Sh, /* Sh */ |
138 | NULL, /* Ss */ | | 138 | NULL, /* Ss */ |
139 | pmdoc_Pp, /* Pp */ | | 139 | pmdoc_Pp, /* Pp */ |
140 | NULL, /* D1 */ | | 140 | NULL, /* D1 */ |
141 | NULL, /* Dl */ | | 141 | NULL, /* Dl */ |
142 | NULL, /* Bd */ | | 142 | NULL, /* Bd */ |
143 | NULL, /* Ed */ | | 143 | NULL, /* Ed */ |
144 | NULL, /* Bl */ | | 144 | NULL, /* Bl */ |
145 | NULL, /* El */ | | 145 | NULL, /* El */ |
146 | NULL, /* It */ | | 146 | NULL, /* It */ |
147 | NULL, /* Ad */ | | 147 | NULL, /* Ad */ |
148 | NULL, /* An */ | | 148 | NULL, /* An */ |
149 | NULL, /* Ar */ | | 149 | NULL, /* Ar */ |
150 | NULL, /* Cd */ | | 150 | NULL, /* Cd */ |
151 | NULL, /* Cm */ | | 151 | NULL, /* Cm */ |
152 | NULL, /* Dv */ | | 152 | NULL, /* Dv */ |
153 | NULL, /* Er */ | | 153 | NULL, /* Er */ |
154 | NULL, /* Ev */ | | 154 | NULL, /* Ev */ |
155 | NULL, /* Ex */ | | 155 | NULL, /* Ex */ |
156 | NULL, /* Fa */ | | 156 | NULL, /* Fa */ |
157 | NULL, /* Fd */ | | 157 | NULL, /* Fd */ |
158 | NULL, /* Fl */ | | 158 | NULL, /* Fl */ |
159 | NULL, /* Fn */ | | 159 | NULL, /* Fn */ |
160 | NULL, /* Ft */ | | 160 | NULL, /* Ft */ |
161 | NULL, /* Ic */ | | 161 | NULL, /* Ic */ |
162 | NULL, /* In */ | | 162 | NULL, /* In */ |
163 | NULL, /* Li */ | | 163 | NULL, /* Li */ |
164 | pmdoc_Nd, /* Nd */ | | 164 | pmdoc_Nd, /* Nd */ |
165 | pmdoc_Nm, /* Nm */ | | 165 | pmdoc_Nm, /* Nm */ |
166 | NULL, /* Op */ | | 166 | NULL, /* Op */ |
167 | NULL, /* Ot */ | | 167 | NULL, /* Ot */ |
168 | NULL, /* Pa */ | | 168 | NULL, /* Pa */ |
169 | NULL, /* Rv */ | | 169 | NULL, /* Rv */ |
170 | NULL, /* St */ | | 170 | NULL, /* St */ |
171 | NULL, /* Va */ | | 171 | NULL, /* Va */ |
172 | NULL, /* Vt */ | | 172 | NULL, /* Vt */ |
173 | pmdoc_Xr, /* Xr */ | | 173 | pmdoc_Xr, /* Xr */ |
174 | NULL, /* %A */ | | 174 | NULL, /* %A */ |
175 | NULL, /* %B */ | | 175 | NULL, /* %B */ |
176 | NULL, /* %D */ | | 176 | NULL, /* %D */ |
177 | NULL, /* %I */ | | 177 | NULL, /* %I */ |
178 | NULL, /* %J */ | | 178 | NULL, /* %J */ |
179 | NULL, /* %N */ | | 179 | NULL, /* %N */ |
180 | NULL, /* %O */ | | 180 | NULL, /* %O */ |
181 | NULL, /* %P */ | | 181 | NULL, /* %P */ |
182 | NULL, /* %R */ | | 182 | NULL, /* %R */ |
183 | NULL, /* %T */ | | 183 | NULL, /* %T */ |
184 | NULL, /* %V */ | | 184 | NULL, /* %V */ |
185 | NULL, /* Ac */ | | 185 | NULL, /* Ac */ |
186 | NULL, /* Ao */ | | 186 | NULL, /* Ao */ |
187 | NULL, /* Aq */ | | 187 | NULL, /* Aq */ |
188 | NULL, /* At */ | | 188 | NULL, /* At */ |
189 | NULL, /* Bc */ | | 189 | NULL, /* Bc */ |
190 | NULL, /* Bf */ | | 190 | NULL, /* Bf */ |
191 | NULL, /* Bo */ | | 191 | NULL, /* Bo */ |
192 | NULL, /* Bq */ | | 192 | NULL, /* Bq */ |
193 | NULL, /* Bsx */ | | 193 | NULL, /* Bsx */ |
194 | NULL, /* Bx */ | | 194 | NULL, /* Bx */ |
195 | NULL, /* Db */ | | 195 | NULL, /* Db */ |
196 | NULL, /* Dc */ | | 196 | NULL, /* Dc */ |
197 | NULL, /* Do */ | | 197 | NULL, /* Do */ |
198 | NULL, /* Dq */ | | 198 | NULL, /* Dq */ |
199 | NULL, /* Ec */ | | 199 | NULL, /* Ec */ |
200 | NULL, /* Ef */ | | 200 | NULL, /* Ef */ |
201 | NULL, /* Em */ | | 201 | NULL, /* Em */ |
202 | NULL, /* Eo */ | | 202 | NULL, /* Eo */ |
203 | NULL, /* Fx */ | | 203 | NULL, /* Fx */ |
204 | NULL, /* Ms */ | | 204 | NULL, /* Ms */ |
205 | NULL, /* No */ | | 205 | NULL, /* No */ |
206 | NULL, /* Ns */ | | 206 | NULL, /* Ns */ |
207 | NULL, /* Nx */ | | 207 | NULL, /* Nx */ |
208 | NULL, /* Ox */ | | 208 | NULL, /* Ox */ |
209 | NULL, /* Pc */ | | 209 | NULL, /* Pc */ |
210 | NULL, /* Pf */ | | 210 | NULL, /* Pf */ |
211 | NULL, /* Po */ | | 211 | NULL, /* Po */ |
212 | NULL, /* Pq */ | | 212 | NULL, /* Pq */ |
213 | NULL, /* Qc */ | | 213 | NULL, /* Qc */ |
214 | NULL, /* Ql */ | | 214 | NULL, /* Ql */ |
215 | NULL, /* Qo */ | | 215 | NULL, /* Qo */ |
216 | NULL, /* Qq */ | | 216 | NULL, /* Qq */ |
217 | NULL, /* Re */ | | 217 | NULL, /* Re */ |
218 | NULL, /* Rs */ | | 218 | NULL, /* Rs */ |
219 | NULL, /* Sc */ | | 219 | NULL, /* Sc */ |
220 | NULL, /* So */ | | 220 | NULL, /* So */ |
221 | NULL, /* Sq */ | | 221 | NULL, /* Sq */ |
222 | NULL, /* Sm */ | | 222 | NULL, /* Sm */ |
223 | NULL, /* Sx */ | | 223 | NULL, /* Sx */ |
224 | NULL, /* Sy */ | | 224 | NULL, /* Sy */ |
225 | NULL, /* Tn */ | | 225 | NULL, /* Tn */ |
226 | NULL, /* Ux */ | | 226 | NULL, /* Ux */ |
227 | NULL, /* Xc */ | | 227 | NULL, /* Xc */ |
228 | NULL, /* Xo */ | | 228 | NULL, /* Xo */ |
229 | NULL, /* Fo */ | | 229 | NULL, /* Fo */ |
230 | NULL, /* Fc */ | | 230 | NULL, /* Fc */ |
231 | NULL, /* Oo */ | | 231 | NULL, /* Oo */ |
232 | NULL, /* Oc */ | | 232 | NULL, /* Oc */ |
233 | NULL, /* Bk */ | | 233 | NULL, /* Bk */ |
234 | NULL, /* Ek */ | | 234 | NULL, /* Ek */ |
235 | NULL, /* Bt */ | | 235 | NULL, /* Bt */ |
236 | NULL, /* Hf */ | | 236 | NULL, /* Hf */ |
237 | NULL, /* Fr */ | | 237 | NULL, /* Fr */ |
238 | NULL, /* Ud */ | | 238 | NULL, /* Ud */ |
239 | NULL, /* Lb */ | | 239 | NULL, /* Lb */ |
240 | NULL, /* Lp */ | | 240 | NULL, /* Lp */ |
241 | NULL, /* Lk */ | | 241 | NULL, /* Lk */ |
242 | NULL, /* Mt */ | | 242 | NULL, /* Mt */ |
243 | NULL, /* Brq */ | | 243 | NULL, /* Brq */ |
244 | NULL, /* Bro */ | | 244 | NULL, /* Bro */ |
245 | NULL, /* Brc */ | | 245 | NULL, /* Brc */ |
246 | NULL, /* %C */ | | 246 | NULL, /* %C */ |
247 | NULL, /* Es */ | | 247 | NULL, /* Es */ |
248 | NULL, /* En */ | | 248 | NULL, /* En */ |
249 | NULL, /* Dx */ | | 249 | NULL, /* Dx */ |
250 | NULL, /* %Q */ | | 250 | NULL, /* %Q */ |
251 | NULL, /* br */ | | 251 | NULL, /* br */ |
252 | NULL, /* sp */ | | 252 | NULL, /* sp */ |
253 | NULL, /* %U */ | | 253 | NULL, /* %U */ |
254 | NULL, /* Ta */ | | 254 | NULL, /* Ta */ |
255 | }; | | 255 | }; |
256 | | | 256 | |
257 | static const pman_nf mans[MAN_MAX] = { | | 257 | static const pman_nf mans[MAN_MAX] = { |
258 | NULL, //br | | 258 | NULL, //br |
259 | NULL, //TH | | 259 | NULL, //TH |
260 | pman_sh, //SH | | 260 | pman_sh, //SH |
261 | NULL, //SS | | 261 | NULL, //SS |
262 | NULL, //TP | | 262 | NULL, //TP |
263 | NULL, //LP | | 263 | NULL, //LP |
264 | NULL, //PP | | 264 | NULL, //PP |
265 | NULL, //P | | 265 | NULL, //P |
266 | NULL, //IP | | 266 | NULL, //IP |
267 | NULL, //HP | | 267 | NULL, //HP |
268 | NULL, //SM | | 268 | NULL, //SM |
269 | NULL, //SB | | 269 | NULL, //SB |
270 | NULL, //BI | | 270 | NULL, //BI |
271 | NULL, //IB | | 271 | NULL, //IB |
272 | NULL, //BR | | 272 | NULL, //BR |
273 | NULL, //RB | | 273 | NULL, //RB |
274 | NULL, //R | | 274 | NULL, //R |
275 | pman_block, //B | | 275 | pman_block, //B |
276 | NULL, //I | | 276 | NULL, //I |
277 | NULL, //IR | | 277 | NULL, //IR |
278 | NULL, //RI | | 278 | NULL, //RI |
279 | NULL, //na | | 279 | NULL, //na |
280 | NULL, //sp | | 280 | NULL, //sp |
281 | NULL, //nf | | 281 | NULL, //nf |
282 | NULL, //fi | | 282 | NULL, //fi |
283 | NULL, //RE | | 283 | NULL, //RE |
284 | NULL, //RS | | 284 | NULL, //RS |
285 | NULL, //DT | | 285 | NULL, //DT |
286 | NULL, //UC | | 286 | NULL, //UC |
287 | NULL, //PD | | 287 | NULL, //PD |
288 | NULL, //AT | | 288 | NULL, //AT |
289 | NULL, //in | | 289 | NULL, //in |
290 | NULL, //ft | | 290 | NULL, //ft |
291 | }; | | 291 | }; |
292 | | | 292 | |
293 | | | 293 | |
294 | int | | 294 | int |
295 | main(int argc, char *argv[]) | | 295 | main(int argc, char *argv[]) |
296 | { | | 296 | { |
297 | FILE *file; | | 297 | FILE *file; |
298 | const char *sqlstr, *manconf = NULL; | | 298 | const char *sqlstr, *manconf = NULL; |
299 | char *line, *command, *parent; | | 299 | char *line, *command, *parent; |
300 | char *errmsg; | | 300 | char *errmsg; |
301 | int ch; | | 301 | int ch; |
302 | struct mparse *mp; | | 302 | struct mparse *mp; |
303 | sqlite3 *db; | | 303 | sqlite3 *db; |
304 | ssize_t len; | | 304 | ssize_t len; |
305 | size_t linesize; | | 305 | size_t linesize; |
306 | struct mandb_rec rec; | | 306 | struct mandb_rec rec; |
307 | | | 307 | |
308 | while ((ch = getopt(argc, argv, "C:floQqv")) != -1) { | | 308 | while ((ch = getopt(argc, argv, "C:floQqv")) != -1) { |
309 | switch (ch) { | | 309 | switch (ch) { |
310 | case 'C': | | 310 | case 'C': |
311 | manconf = optarg; | | 311 | manconf = optarg; |
312 | break; | | 312 | break; |
313 | case 'f': | | 313 | case 'f': |
314 | mflags.recreate = 1; | | 314 | mflags.recreate = 1; |
315 | break; | | 315 | break; |
316 | case 'l': | | 316 | case 'l': |
317 | mflags.limit = 1; | | 317 | mflags.limit = 1; |
318 | break; | | 318 | break; |
319 | case 'o': | | 319 | case 'o': |
320 | mflags.optimize = 1; | | 320 | mflags.optimize = 1; |
321 | break; | | 321 | break; |
322 | case 'Q': | | 322 | case 'Q': |
323 | mflags.verbosity = 0; | | 323 | mflags.verbosity = 0; |
324 | break; | | 324 | break; |
325 | case 'q': | | 325 | case 'q': |
326 | mflags.verbosity = 1; | | 326 | mflags.verbosity = 1; |
327 | break; | | 327 | break; |
328 | case 'v': | | 328 | case 'v': |
329 | mflags.verbosity = 2; | | 329 | mflags.verbosity = 2; |
330 | break; | | 330 | break; |
331 | default: | | 331 | default: |
332 | usage(); | | 332 | usage(); |
333 | } | | 333 | } |
334 | } | | 334 | } |
335 | | | 335 | |
336 | memset(&rec, 0, sizeof(rec)); | | 336 | memset(&rec, 0, sizeof(rec)); |
337 | | | 337 | |
338 | init_secbuffs(&rec); | | 338 | init_secbuffs(&rec); |
339 | mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL); | | 339 | mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL); |
340 | | | 340 | |
341 | if (manconf) { | | 341 | if (manconf) { |
342 | char *arg; | | 342 | char *arg; |
343 | size_t command_len = shquote(manconf, NULL, 0) + 1; | | 343 | size_t command_len = shquote(manconf, NULL, 0) + 1; |
344 | arg = emalloc(command_len); | | 344 | arg = emalloc(command_len); |
345 | shquote(manconf, arg, command_len); | | 345 | shquote(manconf, arg, command_len); |
346 | easprintf(&command, "man -p -C %s", arg); | | 346 | easprintf(&command, "man -p -C %s", arg); |
347 | free(arg); | | 347 | free(arg); |
348 | } else { | | 348 | } else { |
349 | command = estrdup("man -p"); | | 349 | command = estrdup("man -p"); |
350 | manconf = MANCONF; | | 350 | manconf = MANCONF; |
351 | } | | 351 | } |
352 | | | 352 | |
353 | if (mflags.recreate) { | | 353 | if (mflags.recreate) { |
354 | char *dbp = get_dbpath(manconf); | | 354 | char *dbp = get_dbpath(manconf); |
355 | /* No error here, it will fail in init_db in the same call */ | | 355 | /* No error here, it will fail in init_db in the same call */ |
356 | if (dbp != NULL) | | 356 | if (dbp != NULL) |
357 | remove(dbp); | | 357 | remove(dbp); |
358 | } | | 358 | } |
359 | | | 359 | |
360 | if ((db = init_db(MANDB_CREATE, manconf)) == NULL) | | 360 | if ((db = init_db(MANDB_CREATE, manconf)) == NULL) |
361 | exit(EXIT_FAILURE); | | 361 | exit(EXIT_FAILURE); |
362 | | | 362 | |
363 | sqlite3_exec(db, "PRAGMA synchronous = 0", NULL, NULL, &errmsg); | | 363 | sqlite3_exec(db, "PRAGMA synchronous = 0", NULL, NULL, &errmsg); |
364 | if (errmsg != NULL) { | | 364 | if (errmsg != NULL) { |
365 | warnx("%s", errmsg); | | 365 | warnx("%s", errmsg); |
366 | free(errmsg); | | 366 | free(errmsg); |
367 | close_db(db); | | 367 | close_db(db); |
368 | exit(EXIT_FAILURE); | | 368 | exit(EXIT_FAILURE); |
369 | } | | 369 | } |
370 | | | 370 | |
371 | sqlite3_exec(db, "ATTACH DATABASE \':memory:\' AS metadb", NULL, NULL, | | 371 | sqlite3_exec(db, "ATTACH DATABASE \':memory:\' AS metadb", NULL, NULL, |
372 | &errmsg); | | 372 | &errmsg); |
373 | if (errmsg != NULL) { | | 373 | if (errmsg != NULL) { |
374 | warnx("%s", errmsg); | | 374 | warnx("%s", errmsg); |
375 | free(errmsg); | | 375 | free(errmsg); |
376 | close_db(db); | | 376 | close_db(db); |
377 | exit(EXIT_FAILURE); | | 377 | exit(EXIT_FAILURE); |
378 | } | | 378 | } |
379 | | | 379 | |
380 | | | 380 | |
381 | /* Call man -p to get the list of man page dirs */ | | 381 | /* Call man -p to get the list of man page dirs */ |
382 | if ((file = popen(command, "r")) == NULL) { | | 382 | if ((file = popen(command, "r")) == NULL) { |
383 | close_db(db); | | 383 | close_db(db); |
384 | err(EXIT_FAILURE, "fopen failed"); | | 384 | err(EXIT_FAILURE, "fopen failed"); |
385 | } | | 385 | } |
386 | free(command); | | 386 | free(command); |
387 | | | 387 | |
388 | /* Begin the transaction for indexing the pages */ | | 388 | /* Begin the transaction for indexing the pages */ |
389 | sqlite3_exec(db, "BEGIN", NULL, NULL, &errmsg); | | 389 | sqlite3_exec(db, "BEGIN", NULL, NULL, &errmsg); |
390 | if (errmsg != NULL) { | | 390 | if (errmsg != NULL) { |
391 | warnx("%s", errmsg); | | 391 | warnx("%s", errmsg); |
392 | free(errmsg); | | 392 | free(errmsg); |
393 | exit(EXIT_FAILURE); | | 393 | exit(EXIT_FAILURE); |
394 | } | | 394 | } |
395 | | | 395 | |
396 | sqlstr = "CREATE TABLE metadb.file_cache(device, inode, mtime, parent," | | 396 | sqlstr = "CREATE TABLE metadb.file_cache(device, inode, mtime, parent," |
397 | " file PRIMARY KEY);" | | 397 | " file PRIMARY KEY);" |
398 | "CREATE UNIQUE INDEX metadb.index_file_cache_dev" | | 398 | "CREATE UNIQUE INDEX metadb.index_file_cache_dev" |
399 | " ON file_cache (device, inode)"; | | 399 | " ON file_cache (device, inode)"; |
400 | | | 400 | |
401 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); | | 401 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); |
402 | if (errmsg != NULL) { | | 402 | if (errmsg != NULL) { |
403 | warnx("%s", errmsg); | | 403 | warnx("%s", errmsg); |
404 | free(errmsg); | | 404 | free(errmsg); |
405 | close_db(db); | | 405 | close_db(db); |
406 | exit(EXIT_FAILURE); | | 406 | exit(EXIT_FAILURE); |
407 | } | | 407 | } |
408 | | | 408 | |
409 | if (mflags.verbosity) | | 409 | if (mflags.verbosity) |
410 | printf("Building temporary file cache\n"); | | 410 | printf("Building temporary file cache\n"); |
411 | line = NULL; | | 411 | line = NULL; |
412 | linesize = 0; | | 412 | linesize = 0; |
413 | while ((len = getline(&line, &linesize, file)) != -1) { | | 413 | while ((len = getline(&line, &linesize, file)) != -1) { |
414 | /* Replace the new line character at the end of string with '\0' */ | | 414 | /* Replace the new line character at the end of string with '\0' */ |
415 | line[len - 1] = '\0'; | | 415 | line[len - 1] = '\0'; |
416 | parent = estrdup(line); | | 416 | parent = estrdup(line); |
417 | char *pdir = estrdup(dirname(parent)); | | 417 | char *pdir = estrdup(dirname(parent)); |
418 | free(parent); | | 418 | free(parent); |
419 | /* Traverse the man page directories and parse the pages */ | | 419 | /* Traverse the man page directories and parse the pages */ |
420 | traversedir(pdir, line, db, mp); | | 420 | traversedir(pdir, line, db, mp); |
421 | free(pdir); | | 421 | free(pdir); |
422 | } | | 422 | } |
423 | free(line); | | 423 | free(line); |
424 | | | 424 | |
425 | if (pclose(file) == -1) { | | 425 | if (pclose(file) == -1) { |
426 | close_db(db); | | 426 | close_db(db); |
427 | cleanup(&rec); | | 427 | cleanup(&rec); |
428 | free_secbuffs(&rec); | | 428 | free_secbuffs(&rec); |
429 | err(EXIT_FAILURE, "pclose error"); | | 429 | err(EXIT_FAILURE, "pclose error"); |
430 | } | | 430 | } |
431 | | | 431 | |
432 | if (mflags.verbosity) | | 432 | if (mflags.verbosity) |
433 | printf("Performing index update\n"); | | 433 | printf("Performing index update\n"); |
434 | update_db(db, mp, &rec); | | 434 | update_db(db, mp, &rec); |
435 | mparse_free(mp); | | 435 | mparse_free(mp); |
436 | free_secbuffs(&rec); | | 436 | free_secbuffs(&rec); |
437 | | | 437 | |
438 | /* Commit the transaction */ | | 438 | /* Commit the transaction */ |
439 | sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg); | | 439 | sqlite3_exec(db, "COMMIT", NULL, NULL, &errmsg); |
440 | if (errmsg != NULL) { | | 440 | if (errmsg != NULL) { |
441 | warnx("%s", errmsg); | | 441 | warnx("%s", errmsg); |
442 | free(errmsg); | | 442 | free(errmsg); |
443 | exit(EXIT_FAILURE); | | 443 | exit(EXIT_FAILURE); |
444 | } | | 444 | } |
445 | | | 445 | |
446 | if (mflags.optimize) | | 446 | if (mflags.optimize) |
447 | optimize(db); | | 447 | optimize(db); |
448 | | | 448 | |
449 | close_db(db); | | 449 | close_db(db); |
450 | return 0; | | 450 | return 0; |
451 | } | | 451 | } |
452 | | | 452 | |
453 | /* | | 453 | /* |
454 | * traversedir -- | | 454 | * traversedir -- |
455 | * Traverses the given directory recursively and passes all the man page files | | 455 | * Traverses the given directory recursively and passes all the man page files |
456 | * in the way to build_file_cache() | | 456 | * in the way to build_file_cache() |
457 | */ | | 457 | */ |
458 | static void | | 458 | static void |
459 | traversedir(const char *parent, const char *file, sqlite3 *db, | | 459 | traversedir(const char *parent, const char *file, sqlite3 *db, |
460 | struct mparse *mp) | | 460 | struct mparse *mp) |
461 | { | | 461 | { |
462 | struct stat sb; | | 462 | struct stat sb; |
463 | struct dirent *dirp; | | 463 | struct dirent *dirp; |
464 | DIR *dp; | | 464 | DIR *dp; |
465 | char *buf; | | 465 | char *buf; |
466 | | | 466 | |
467 | if (stat(file, &sb) < 0) { | | 467 | if (stat(file, &sb) < 0) { |
468 | if (mflags.verbosity) | | 468 | if (mflags.verbosity) |
469 | warn("stat failed: %s", file); | | 469 | warn("stat failed: %s", file); |
470 | return; | | 470 | return; |
471 | } | | 471 | } |
472 | | | 472 | |
473 | /* If it is a directory, traverse it recursively */ | | 473 | /* If it is a directory, traverse it recursively */ |
474 | if (S_ISDIR(sb.st_mode)) { | | 474 | if (S_ISDIR(sb.st_mode)) { |
475 | if ((dp = opendir(file)) == NULL) { | | 475 | if ((dp = opendir(file)) == NULL) { |
476 | if (mflags.verbosity) | | 476 | if (mflags.verbosity) |
477 | warn("opendir error: %s", file); | | 477 | warn("opendir error: %s", file); |
478 | return; | | 478 | return; |
479 | } | | 479 | } |
480 | | | 480 | |
481 | while ((dirp = readdir(dp)) != NULL) { | | 481 | while ((dirp = readdir(dp)) != NULL) { |
482 | /* Avoid . and .. entries in a directory */ | | 482 | /* Avoid . and .. entries in a directory */ |
483 | if (strncmp(dirp->d_name, ".", 1)) { | | 483 | if (strncmp(dirp->d_name, ".", 1)) { |
484 | easprintf(&buf, "%s/%s", file, dirp->d_name); | | 484 | easprintf(&buf, "%s/%s", file, dirp->d_name); |
485 | traversedir(parent, buf, db, mp); | | 485 | traversedir(parent, buf, db, mp); |
486 | free(buf); | | 486 | free(buf); |
487 | } | | 487 | } |
488 | } | | 488 | } |
489 | closedir(dp); | | 489 | closedir(dp); |
490 | } | | 490 | } |
491 | | | 491 | |
492 | if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) | | 492 | if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) |
493 | return; | | 493 | return; |
494 | | | 494 | |
495 | if (sb.st_size == 0) { | | 495 | if (sb.st_size == 0) { |
496 | if (mflags.verbosity) | | 496 | if (mflags.verbosity) |
497 | warnx("Empty file: %s", file); | | 497 | warnx("Empty file: %s", file); |
498 | return; | | 498 | return; |
499 | } | | 499 | } |
500 | build_file_cache(db, parent, file, &sb); | | 500 | build_file_cache(db, parent, file, &sb); |
501 | } | | 501 | } |
502 | | | 502 | |
503 | /* build_file_cache -- | | 503 | /* build_file_cache -- |
504 | * This function generates an md5 hash of the file passed as its 2nd parameter | | 504 | * This function generates an md5 hash of the file passed as its 2nd parameter |
505 | * and stores it in a temporary table file_cache along with the full file path. | | 505 | * and stores it in a temporary table file_cache along with the full file path. |
506 | * This is done to support incremental updation of the database. | | 506 | * This is done to support incremental updation of the database. |
507 | * The temporary table file_cache is dropped thereafter in the function | | 507 | * The temporary table file_cache is dropped thereafter in the function |
508 | * update_db(), once the database has been updated. | | 508 | * update_db(), once the database has been updated. |
509 | */ | | 509 | */ |
510 | static void | | 510 | static void |
511 | build_file_cache(sqlite3 *db, const char *parent, const char *file, | | 511 | build_file_cache(sqlite3 *db, const char *parent, const char *file, |
512 | struct stat *sb) | | 512 | struct stat *sb) |
513 | { | | 513 | { |
514 | const char *sqlstr; | | 514 | const char *sqlstr; |
515 | sqlite3_stmt *stmt = NULL; | | 515 | sqlite3_stmt *stmt = NULL; |
516 | int rc, idx; | | 516 | int rc, idx; |
517 | assert(file != NULL); | | 517 | assert(file != NULL); |
518 | dev_t device_cache = sb->st_dev; | | 518 | dev_t device_cache = sb->st_dev; |
519 | ino_t inode_cache = sb->st_ino; | | 519 | ino_t inode_cache = sb->st_ino; |
520 | time_t mtime_cache = sb->st_mtime; | | 520 | time_t mtime_cache = sb->st_mtime; |
521 | | | 521 | |
522 | sqlstr = "INSERT INTO metadb.file_cache VALUES (:device, :inode," | | 522 | sqlstr = "INSERT INTO metadb.file_cache VALUES (:device, :inode," |
523 | " :mtime, :parent, :file)"; | | 523 | " :mtime, :parent, :file)"; |
524 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 524 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
525 | if (rc != SQLITE_OK) { | | 525 | if (rc != SQLITE_OK) { |
526 | if (mflags.verbosity) | | 526 | if (mflags.verbosity) |
527 | warnx("%s", sqlite3_errmsg(db)); | | 527 | warnx("%s", sqlite3_errmsg(db)); |
528 | return; | | 528 | return; |
529 | } | | 529 | } |
530 | | | 530 | |
531 | idx = sqlite3_bind_parameter_index(stmt, ":device"); | | 531 | idx = sqlite3_bind_parameter_index(stmt, ":device"); |
532 | rc = sqlite3_bind_int64(stmt, idx, device_cache); | | 532 | rc = sqlite3_bind_int64(stmt, idx, device_cache); |
533 | if (rc != SQLITE_OK) { | | 533 | if (rc != SQLITE_OK) { |
534 | if (mflags.verbosity) | | 534 | if (mflags.verbosity) |
535 | warnx("%s", sqlite3_errmsg(db)); | | 535 | warnx("%s", sqlite3_errmsg(db)); |
536 | sqlite3_finalize(stmt); | | 536 | sqlite3_finalize(stmt); |
537 | return; | | 537 | return; |
538 | } | | 538 | } |
539 | | | 539 | |
540 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); | | 540 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); |
541 | rc = sqlite3_bind_int64(stmt, idx, inode_cache); | | 541 | rc = sqlite3_bind_int64(stmt, idx, inode_cache); |
542 | if (rc != SQLITE_OK) { | | 542 | if (rc != SQLITE_OK) { |
543 | if (mflags.verbosity) | | 543 | if (mflags.verbosity) |
544 | warnx("%s", sqlite3_errmsg(db)); | | 544 | warnx("%s", sqlite3_errmsg(db)); |
545 | sqlite3_finalize(stmt); | | 545 | sqlite3_finalize(stmt); |
546 | return; | | 546 | return; |
547 | } | | 547 | } |
548 | | | 548 | |
549 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); | | 549 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); |
550 | rc = sqlite3_bind_int64(stmt, idx, mtime_cache); | | 550 | rc = sqlite3_bind_int64(stmt, idx, mtime_cache); |
551 | if (rc != SQLITE_OK) { | | 551 | if (rc != SQLITE_OK) { |
552 | if (mflags.verbosity) | | 552 | if (mflags.verbosity) |
553 | warnx("%s", sqlite3_errmsg(db)); | | 553 | warnx("%s", sqlite3_errmsg(db)); |
554 | sqlite3_finalize(stmt); | | 554 | sqlite3_finalize(stmt); |
555 | return; | | 555 | return; |
556 | } | | 556 | } |
557 | | | 557 | |
558 | idx = sqlite3_bind_parameter_index(stmt, ":parent"); | | 558 | idx = sqlite3_bind_parameter_index(stmt, ":parent"); |
559 | rc = sqlite3_bind_text(stmt, idx, parent, -1, NULL); | | 559 | rc = sqlite3_bind_text(stmt, idx, parent, -1, NULL); |
560 | if (rc != SQLITE_OK) { | | 560 | if (rc != SQLITE_OK) { |
561 | if (mflags.verbosity) | | 561 | if (mflags.verbosity) |
562 | warnx("%s", sqlite3_errmsg(db)); | | 562 | warnx("%s", sqlite3_errmsg(db)); |
563 | sqlite3_finalize(stmt); | | 563 | sqlite3_finalize(stmt); |
564 | return; | | 564 | return; |
565 | } | | 565 | } |
566 | | | 566 | |
567 | idx = sqlite3_bind_parameter_index(stmt, ":file"); | | 567 | idx = sqlite3_bind_parameter_index(stmt, ":file"); |
568 | rc = sqlite3_bind_text(stmt, idx, file, -1, NULL); | | 568 | rc = sqlite3_bind_text(stmt, idx, file, -1, NULL); |
569 | if (rc != SQLITE_OK) { | | 569 | if (rc != SQLITE_OK) { |
570 | if (mflags.verbosity) | | 570 | if (mflags.verbosity) |
571 | warnx("%s", sqlite3_errmsg(db)); | | 571 | warnx("%s", sqlite3_errmsg(db)); |
572 | sqlite3_finalize(stmt); | | 572 | sqlite3_finalize(stmt); |
573 | return; | | 573 | return; |
574 | } | | 574 | } |
575 | | | 575 | |
576 | sqlite3_step(stmt); | | 576 | sqlite3_step(stmt); |
577 | sqlite3_finalize(stmt); | | 577 | sqlite3_finalize(stmt); |
578 | } | | 578 | } |
579 | | | 579 | |
580 | static void | | 580 | static void |
581 | update_existing_entry(sqlite3 *db, const char *file, const char *hash, | | 581 | update_existing_entry(sqlite3 *db, const char *file, const char *hash, |
582 | mandb_rec *rec, int *new_count, int *link_count, int *err_count) | | 582 | mandb_rec *rec, int *new_count, int *link_count, int *err_count) |
583 | { | | 583 | { |
584 | int update_count, rc, idx; | | 584 | int update_count, rc, idx; |
585 | const char *inner_sqlstr; | | 585 | const char *inner_sqlstr; |
586 | sqlite3_stmt *inner_stmt; | | 586 | sqlite3_stmt *inner_stmt; |
587 | | | 587 | |
588 | update_count = sqlite3_total_changes(db); | | 588 | update_count = sqlite3_total_changes(db); |
589 | inner_sqlstr = "UPDATE mandb_meta SET device = :device," | | 589 | inner_sqlstr = "UPDATE mandb_meta SET device = :device," |
590 | " inode = :inode, mtime = :mtime WHERE" | | 590 | " inode = :inode, mtime = :mtime WHERE" |
591 | " md5_hash = :md5 AND file = :file AND" | | 591 | " md5_hash = :md5 AND file = :file AND" |
592 | " (device <> :device2 OR inode <> " | | 592 | " (device <> :device2 OR inode <> " |
593 | " :inode2 OR mtime <> :mtime2)"; | | 593 | " :inode2 OR mtime <> :mtime2)"; |
594 | rc = sqlite3_prepare_v2(db, inner_sqlstr, -1, &inner_stmt, NULL); | | 594 | rc = sqlite3_prepare_v2(db, inner_sqlstr, -1, &inner_stmt, NULL); |
595 | if (rc != SQLITE_OK) { | | 595 | if (rc != SQLITE_OK) { |
596 | if (mflags.verbosity) | | 596 | if (mflags.verbosity) |
597 | warnx("%s", sqlite3_errmsg(db)); | | 597 | warnx("%s", sqlite3_errmsg(db)); |
598 | return; | | 598 | return; |
599 | } | | 599 | } |
600 | idx = sqlite3_bind_parameter_index(inner_stmt, ":device"); | | 600 | idx = sqlite3_bind_parameter_index(inner_stmt, ":device"); |
601 | sqlite3_bind_int64(inner_stmt, idx, rec->device); | | 601 | sqlite3_bind_int64(inner_stmt, idx, rec->device); |
602 | idx = sqlite3_bind_parameter_index(inner_stmt, ":inode"); | | 602 | idx = sqlite3_bind_parameter_index(inner_stmt, ":inode"); |
603 | sqlite3_bind_int64(inner_stmt, idx, rec->inode); | | 603 | sqlite3_bind_int64(inner_stmt, idx, rec->inode); |
604 | idx = sqlite3_bind_parameter_index(inner_stmt, ":mtime"); | | 604 | idx = sqlite3_bind_parameter_index(inner_stmt, ":mtime"); |
605 | sqlite3_bind_int64(inner_stmt, idx, rec->mtime); | | 605 | sqlite3_bind_int64(inner_stmt, idx, rec->mtime); |
606 | idx = sqlite3_bind_parameter_index(inner_stmt, ":md5"); | | 606 | idx = sqlite3_bind_parameter_index(inner_stmt, ":md5"); |
607 | sqlite3_bind_text(inner_stmt, idx, hash, -1, NULL); | | 607 | sqlite3_bind_text(inner_stmt, idx, hash, -1, NULL); |
608 | idx = sqlite3_bind_parameter_index(inner_stmt, ":file"); | | 608 | idx = sqlite3_bind_parameter_index(inner_stmt, ":file"); |
609 | sqlite3_bind_text(inner_stmt, idx, file, -1, NULL); | | 609 | sqlite3_bind_text(inner_stmt, idx, file, -1, NULL); |
610 | idx = sqlite3_bind_parameter_index(inner_stmt, ":device2"); | | 610 | idx = sqlite3_bind_parameter_index(inner_stmt, ":device2"); |
611 | sqlite3_bind_int64(inner_stmt, idx, rec->device); | | 611 | sqlite3_bind_int64(inner_stmt, idx, rec->device); |
612 | idx = sqlite3_bind_parameter_index(inner_stmt, ":inode2"); | | 612 | idx = sqlite3_bind_parameter_index(inner_stmt, ":inode2"); |
613 | sqlite3_bind_int64(inner_stmt, idx, rec->inode); | | 613 | sqlite3_bind_int64(inner_stmt, idx, rec->inode); |
614 | idx = sqlite3_bind_parameter_index(inner_stmt, ":mtime2"); | | 614 | idx = sqlite3_bind_parameter_index(inner_stmt, ":mtime2"); |
615 | sqlite3_bind_int64(inner_stmt, idx, rec->mtime); | | 615 | sqlite3_bind_int64(inner_stmt, idx, rec->mtime); |
616 | | | 616 | |
617 | rc = sqlite3_step(inner_stmt); | | 617 | rc = sqlite3_step(inner_stmt); |
618 | if (rc == SQLITE_DONE) { | | 618 | if (rc == SQLITE_DONE) { |
619 | /* Check if an update has been performed. */ | | 619 | /* Check if an update has been performed. */ |
620 | if (update_count != sqlite3_total_changes(db)) { | | 620 | if (update_count != sqlite3_total_changes(db)) { |
621 | if (mflags.verbosity == 2) | | 621 | if (mflags.verbosity == 2) |
622 | printf("Updated %s\n", file); | | 622 | printf("Updated %s\n", file); |
623 | (*new_count)++; | | 623 | (*new_count)++; |
624 | } else { | | 624 | } else { |
625 | /* Otherwise it was a hardlink. */ | | 625 | /* Otherwise it was a hardlink. */ |
626 | (*link_count)++; | | 626 | (*link_count)++; |
627 | } | | 627 | } |
628 | } else { | | 628 | } else { |
629 | if (mflags.verbosity == 2) | | 629 | if (mflags.verbosity == 2) |
630 | warnx("Could not update the meta data for %s", file); | | 630 | warnx("Could not update the meta data for %s", file); |
631 | (*err_count)++; | | 631 | (*err_count)++; |
632 | } | | 632 | } |
633 | sqlite3_finalize(inner_stmt); | | 633 | sqlite3_finalize(inner_stmt); |
634 | } | | 634 | } |
635 | | | 635 | |
636 | /* read_and_decompress -- | | 636 | /* read_and_decompress -- |
637 | * Reads the given file into memory. If it is compressed, decompress | | 637 | * Reads the given file into memory. If it is compressed, decompress |
638 | * it before returning to the caller. | | 638 | * it before returning to the caller. |
639 | */ | | 639 | */ |
640 | static int | | 640 | static int |
641 | read_and_decompress(const char *file, void **bufp, size_t *len) | | 641 | read_and_decompress(const char *file, void **bufp, size_t *len) |
642 | { | | 642 | { |
643 | size_t off; | | 643 | size_t off; |
644 | ssize_t r; | | 644 | ssize_t r; |
645 | struct archive *a; | | 645 | struct archive *a; |
646 | struct archive_entry *ae; | | 646 | struct archive_entry *ae; |
647 | char *buf; | | 647 | char *buf; |
648 | | | 648 | |
649 | if ((a = archive_read_new()) == NULL) | | 649 | if ((a = archive_read_new()) == NULL) |
650 | errx(EXIT_FAILURE, "memory allocation failed"); | | 650 | errx(EXIT_FAILURE, "memory allocation failed"); |
651 | | | 651 | |
652 | *bufp = NULL; | | 652 | *bufp = NULL; |
653 | if (archive_read_support_compression_all(a) != ARCHIVE_OK || | | 653 | if (archive_read_support_compression_all(a) != ARCHIVE_OK || |
654 | archive_read_support_format_raw(a) != ARCHIVE_OK || | | 654 | archive_read_support_format_raw(a) != ARCHIVE_OK || |
655 | archive_read_open_filename(a, file, 65536) != ARCHIVE_OK || | | 655 | archive_read_open_filename(a, file, 65536) != ARCHIVE_OK || |
656 | archive_read_next_header(a, &ae) != ARCHIVE_OK) | | 656 | archive_read_next_header(a, &ae) != ARCHIVE_OK) |
657 | goto archive_error; | | 657 | goto archive_error; |
658 | *len = 65536; | | 658 | *len = 65536; |
659 | buf = emalloc(*len); | | 659 | buf = emalloc(*len); |
660 | off = 0; | | 660 | off = 0; |
661 | for (;;) { | | 661 | for (;;) { |
662 | r = archive_read_data(a, buf + off, *len - off); | | 662 | r = archive_read_data(a, buf + off, *len - off); |
663 | if (r == ARCHIVE_OK) { | | 663 | if (r == ARCHIVE_OK) { |
664 | archive_read_close(a); | | 664 | archive_read_close(a); |
665 | *bufp = buf; | | 665 | *bufp = buf; |
666 | *len = off; | | 666 | *len = off; |
667 | return 0; | | 667 | return 0; |
668 | } | | 668 | } |
669 | if (r <= 0) { | | 669 | if (r <= 0) { |
670 | free(buf); | | 670 | free(buf); |
671 | break; | | 671 | break; |
672 | } | | 672 | } |
673 | off += r; | | 673 | off += r; |
674 | if (off == *len) { | | 674 | if (off == *len) { |
675 | *len *= 2; | | 675 | *len *= 2; |
676 | if (*len < off) { | | 676 | if (*len < off) { |
677 | if (mflags.verbosity) | | 677 | if (mflags.verbosity) |
678 | warnx("File too large: %s", file); | | 678 | warnx("File too large: %s", file); |
679 | free(buf); | | 679 | free(buf); |
680 | archive_read_close(a); | | 680 | archive_read_close(a); |
681 | return -1; | | 681 | return -1; |
682 | } | | 682 | } |
683 | buf = erealloc(buf, *len); | | 683 | buf = erealloc(buf, *len); |
684 | } | | 684 | } |
685 | } | | 685 | } |
686 | | | 686 | |
687 | archive_error: | | 687 | archive_error: |
688 | warnx("Error while reading `%s': %s", file, archive_error_string(a)); | | 688 | warnx("Error while reading `%s': %s", file, archive_error_string(a)); |
689 | archive_read_close(a); | | 689 | archive_read_close(a); |
690 | return -1; | | 690 | return -1; |
691 | } | | 691 | } |
692 | | | 692 | |
693 | /* update_db -- | | 693 | /* update_db -- |
694 | * Does an incremental updation of the database by checking the file_cache. | | 694 | * Does an incremental updation of the database by checking the file_cache. |
695 | * It parses and adds the pages which are present in file_cache, | | 695 | * It parses and adds the pages which are present in file_cache, |
696 | * but not in the database. | | 696 | * but not in the database. |
697 | * It also removes the pages which are present in the databse, | | 697 | * It also removes the pages which are present in the databse, |
698 | * but not in the file_cache. | | 698 | * but not in the file_cache. |
699 | */ | | 699 | */ |
700 | static void | | 700 | static void |
701 | update_db(sqlite3 *db, struct mparse *mp, mandb_rec *rec) | | 701 | update_db(sqlite3 *db, struct mparse *mp, mandb_rec *rec) |
702 | { | | 702 | { |
703 | const char *sqlstr; | | 703 | const char *sqlstr; |
704 | sqlite3_stmt *stmt = NULL; | | 704 | sqlite3_stmt *stmt = NULL; |
705 | char *file; | | 705 | char *file; |
706 | char *parent; | | 706 | char *parent; |
707 | char *errmsg = NULL; | | 707 | char *errmsg = NULL; |
708 | char *md5sum; | | 708 | char *md5sum; |
709 | void *buf; | | 709 | void *buf; |
710 | size_t buflen; | | 710 | size_t buflen; |
711 | struct sql_row { | | 711 | struct sql_row { |
712 | struct sql_row *next; | | 712 | struct sql_row *next; |
713 | dev_t device; | | 713 | dev_t device; |
714 | ino_t inode; | | 714 | ino_t inode; |
715 | time_t mtime; | | 715 | time_t mtime; |
716 | char *parent; | | 716 | char *parent; |
717 | char *file; | | 717 | char *file; |
718 | } *rows, *row; | | 718 | } *rows, *row; |
719 | int new_count = 0; /* Counter for newly indexed/updated pages */ | | 719 | int new_count = 0; /* Counter for newly indexed/updated pages */ |
720 | int total_count = 0; /* Counter for total number of pages */ | | 720 | int total_count = 0; /* Counter for total number of pages */ |
721 | int err_count = 0; /* Counter for number of failed pages */ | | 721 | int err_count = 0; /* Counter for number of failed pages */ |
722 | int link_count = 0; /* Counter for number of hard/sym links */ | | 722 | int link_count = 0; /* Counter for number of hard/sym links */ |
723 | int md5_status; | | 723 | int md5_status; |
724 | int rc; | | 724 | int rc; |
725 | | | 725 | |
726 | sqlstr = "SELECT device, inode, mtime, parent, file" | | 726 | sqlstr = "SELECT device, inode, mtime, parent, file" |
727 | " FROM metadb.file_cache fc" | | 727 | " FROM metadb.file_cache fc" |
728 | " WHERE NOT EXISTS(SELECT 1 FROM mandb_meta WHERE" | | 728 | " WHERE NOT EXISTS(SELECT 1 FROM mandb_meta WHERE" |
729 | " device = fc.device AND inode = fc.inode AND " | | 729 | " device = fc.device AND inode = fc.inode AND " |
730 | " mtime = fc.mtime AND file = fc.file)"; | | 730 | " mtime = fc.mtime AND file = fc.file)"; |
731 | | | 731 | |
732 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 732 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
733 | if (rc != SQLITE_OK) { | | 733 | if (rc != SQLITE_OK) { |
734 | if (mflags.verbosity) | | 734 | if (mflags.verbosity) |
735 | warnx("%s", sqlite3_errmsg(db)); | | 735 | warnx("%s", sqlite3_errmsg(db)); |
736 | close_db(db); | | 736 | close_db(db); |
737 | errx(EXIT_FAILURE, "Could not query file cache"); | | 737 | errx(EXIT_FAILURE, "Could not query file cache"); |
738 | } | | 738 | } |
739 | | | 739 | |
740 | buf = NULL; | | 740 | buf = NULL; |
741 | rows = NULL; | | 741 | rows = NULL; |
742 | while (sqlite3_step(stmt) == SQLITE_ROW) { | | 742 | while (sqlite3_step(stmt) == SQLITE_ROW) { |
743 | row = emalloc(sizeof(struct sql_row)); | | 743 | row = emalloc(sizeof(struct sql_row)); |
744 | row->device = sqlite3_column_int64(stmt, 0); | | 744 | row->device = sqlite3_column_int64(stmt, 0); |
745 | row->inode = sqlite3_column_int64(stmt, 1); | | 745 | row->inode = sqlite3_column_int64(stmt, 1); |
746 | row->mtime = sqlite3_column_int64(stmt, 2); | | 746 | row->mtime = sqlite3_column_int64(stmt, 2); |
747 | row->parent = estrdup((const char *) sqlite3_column_text(stmt, 3)); | | 747 | row->parent = estrdup((const char *) sqlite3_column_text(stmt, 3)); |
748 | row->file = estrdup((const char *) sqlite3_column_text(stmt, 4)); | | 748 | row->file = estrdup((const char *) sqlite3_column_text(stmt, 4)); |
749 | row->next = rows; | | 749 | row->next = rows; |
750 | rows = row; | | 750 | rows = row; |
751 | total_count++; | | 751 | total_count++; |
752 | } | | 752 | } |
753 | sqlite3_finalize(stmt); | | 753 | sqlite3_finalize(stmt); |
754 | | | 754 | |
755 | for ( ; rows != NULL; free(parent), free(file), free(buf)) { | | 755 | for ( ; rows != NULL; free(parent), free(file), free(buf)) { |
756 | row = rows; | | 756 | row = rows; |
757 | rows = rows->next; | | 757 | rows = rows->next; |
758 | | | 758 | |
759 | rec->device = row->device; | | 759 | rec->device = row->device; |
760 | rec->inode = row->inode; | | 760 | rec->inode = row->inode; |
761 | rec->mtime = row->mtime; | | 761 | rec->mtime = row->mtime; |
762 | parent = row->parent; | | 762 | parent = row->parent; |
763 | file = row->file; | | 763 | file = row->file; |
764 | free(row); | | 764 | free(row); |
765 | | | 765 | |
766 | if (read_and_decompress(file, &buf, &buflen)) { | | 766 | if (read_and_decompress(file, &buf, &buflen)) { |
767 | err_count++; | | 767 | err_count++; |
768 | continue; | | 768 | continue; |
769 | } | | 769 | } |
770 | md5_status = check_md5(file, db, "mandb_meta", &md5sum, buf, buflen); | | 770 | md5_status = check_md5(file, db, "mandb_meta", &md5sum, buf, buflen); |
771 | assert(md5sum != NULL); | | 771 | assert(md5sum != NULL); |
772 | if (md5_status == -1) { | | 772 | if (md5_status == -1) { |
773 | if (mflags.verbosity) | | 773 | if (mflags.verbosity) |
774 | warnx("An error occurred in checking md5 value" | | 774 | warnx("An error occurred in checking md5 value" |
775 | " for file %s", file); | | 775 | " for file %s", file); |
776 | err_count++; | | 776 | err_count++; |
777 | continue; | | 777 | continue; |
778 | } | | 778 | } |
779 | | | 779 | |
780 | if (md5_status == 0) { | | 780 | if (md5_status == 0) { |
781 | /* | | 781 | /* |
782 | * The MD5 hash is already present in the database, | | 782 | * The MD5 hash is already present in the database, |
783 | * so simply update the metadata, ignoring symlinks. | | 783 | * so simply update the metadata, ignoring symlinks. |
784 | */ | | 784 | */ |
785 | struct stat sb; | | 785 | struct stat sb; |
786 | stat(file, &sb); | | 786 | stat(file, &sb); |
787 | if (S_ISLNK(sb.st_mode)) { | | 787 | if (S_ISLNK(sb.st_mode)) { |
788 | free(md5sum); | | 788 | free(md5sum); |
789 | link_count++; | | 789 | link_count++; |
790 | continue; | | 790 | continue; |
791 | } | | 791 | } |
792 | update_existing_entry(db, file, md5sum, rec, | | 792 | update_existing_entry(db, file, md5sum, rec, |
793 | &new_count, &link_count, &err_count); | | 793 | &new_count, &link_count, &err_count); |
794 | free(md5sum); | | 794 | free(md5sum); |
795 | continue; | | 795 | continue; |
796 | } | | 796 | } |
797 | | | 797 | |
798 | if (md5_status == 1) { | | 798 | if (md5_status == 1) { |
799 | /* | | 799 | /* |
800 | * The MD5 hash was not present in the database. | | 800 | * The MD5 hash was not present in the database. |
801 | * This means is either a new file or an updated file. | | 801 | * This means is either a new file or an updated file. |
802 | * We should go ahead with parsing. | | 802 | * We should go ahead with parsing. |
803 | */ | | 803 | */ |
804 | if (mflags.verbosity == 2) | | 804 | if (mflags.verbosity == 2) |
805 | printf("Parsing: %s\n", file); | | 805 | printf("Parsing: %s\n", file); |
806 | rec->md5_hash = md5sum; | | 806 | rec->md5_hash = md5sum; |
807 | rec->file_path = estrdup(file); | | 807 | rec->file_path = estrdup(file); |
808 | // file_path is freed by insert_into_db itself. | | 808 | // file_path is freed by insert_into_db itself. |
809 | chdir(parent); | | 809 | chdir(parent); |
810 | begin_parse(file, mp, rec, buf, buflen); | | 810 | begin_parse(file, mp, rec, buf, buflen); |
811 | if (insert_into_db(db, rec) < 0) { | | 811 | if (insert_into_db(db, rec) < 0) { |
812 | if (mflags.verbosity) | | 812 | if (mflags.verbosity) |
813 | warnx("Error in indexing %s", file); | | 813 | warnx("Error in indexing %s", file); |
814 | err_count++; | | 814 | err_count++; |
815 | } else { | | 815 | } else { |
816 | new_count++; | | 816 | new_count++; |
817 | } | | 817 | } |
818 | } | | 818 | } |
819 | } | | 819 | } |
820 | | | 820 | |
821 | if (mflags.verbosity == 2) { | | 821 | if (mflags.verbosity == 2) { |
822 | printf("Total Number of new or updated pages encountered = %d\n" | | 822 | printf("Total Number of new or updated pages encountered = %d\n" |
823 | "Total number of (hard or symbolic) links found = %d\n" | | 823 | "Total number of (hard or symbolic) links found = %d\n" |
824 | "Total number of pages that were successfully" | | 824 | "Total number of pages that were successfully" |
825 | " indexed/updated = %d\n" | | 825 | " indexed/updated = %d\n" |
826 | "Total number of pages that could not be indexed" | | 826 | "Total number of pages that could not be indexed" |
827 | " due to errors = %d\n", | | 827 | " due to errors = %d\n", |
828 | total_count - link_count, link_count, new_count, err_count); | | 828 | total_count - link_count, link_count, new_count, err_count); |
829 | } | | 829 | } |
830 | | | 830 | |
831 | if (mflags.recreate) | | 831 | if (mflags.recreate) |
832 | return; | | 832 | return; |
833 | | | 833 | |
834 | if (mflags.verbosity == 2) | | 834 | if (mflags.verbosity == 2) |
835 | printf("Deleting stale index entries\n"); | | 835 | printf("Deleting stale index entries\n"); |
836 | | | 836 | |
837 | sqlstr = "DELETE FROM mandb_meta WHERE file NOT IN" | | 837 | sqlstr = "DELETE FROM mandb_meta WHERE file NOT IN" |
838 | " (SELECT file FROM metadb.file_cache);" | | 838 | " (SELECT file FROM metadb.file_cache);" |
839 | "DELETE FROM mandb_links WHERE md5_hash NOT IN" | | 839 | "DELETE FROM mandb_links WHERE md5_hash NOT IN" |
840 | " (SELECT md5_hash from mandb_meta);" | | 840 | " (SELECT md5_hash from mandb_meta);" |
841 | "DROP TABLE metadb.file_cache;" | | 841 | "DROP TABLE metadb.file_cache;" |
842 | "DELETE FROM mandb WHERE rowid NOT IN" | | 842 | "DELETE FROM mandb WHERE rowid NOT IN" |
843 | " (SELECT id FROM mandb_meta);"; | | 843 | " (SELECT id FROM mandb_meta);"; |
844 | | | 844 | |
845 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); | | 845 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); |
846 | if (errmsg != NULL) { | | 846 | if (errmsg != NULL) { |
847 | warnx("Removing old entries failed: %s", errmsg); | | 847 | warnx("Removing old entries failed: %s", errmsg); |
848 | warnx("Please rebuild database from scratch with -f."); | | 848 | warnx("Please rebuild database from scratch with -f."); |
849 | free(errmsg); | | 849 | free(errmsg); |
850 | return; | | 850 | return; |
851 | } | | 851 | } |
852 | } | | 852 | } |
853 | | | 853 | |
854 | /* | | 854 | /* |
855 | * begin_parse -- | | 855 | * begin_parse -- |
856 | * parses the man page using libmandoc | | 856 | * parses the man page using libmandoc |
857 | */ | | 857 | */ |
858 | static void | | 858 | static void |
859 | begin_parse(const char *file, struct mparse *mp, mandb_rec *rec, | | 859 | begin_parse(const char *file, struct mparse *mp, mandb_rec *rec, |
860 | const void *buf, size_t len) | | 860 | const void *buf, size_t len) |
861 | { | | 861 | { |
862 | struct mdoc *mdoc; | | 862 | struct mdoc *mdoc; |
863 | struct man *man; | | 863 | struct man *man; |
864 | mparse_reset(mp); | | 864 | mparse_reset(mp); |
865 | | | 865 | |
866 | rec->xr_found = 0; | | 866 | rec->xr_found = 0; |
867 | | | 867 | |
868 | if (mparse_readmem(mp, buf, len, file) >= MANDOCLEVEL_FATAL) { | | 868 | if (mparse_readmem(mp, buf, len, file) >= MANDOCLEVEL_FATAL) { |
869 | /* Printing this warning at verbosity level 2 | | 869 | /* Printing this warning at verbosity level 2 |
870 | * because some packages from pkgsrc might trigger several | | 870 | * because some packages from pkgsrc might trigger several |
871 | * of such warnings. | | 871 | * of such warnings. |
872 | */ | | 872 | */ |
873 | if (mflags.verbosity == 2) | | 873 | if (mflags.verbosity == 2) |
874 | warnx("%s: Parse failure", file); | | 874 | warnx("%s: Parse failure", file); |
875 | return; | | 875 | return; |
876 | } | | 876 | } |
877 | | | 877 | |
878 | mparse_result(mp, &mdoc, &man); | | 878 | mparse_result(mp, &mdoc, &man); |
879 | if (mdoc == NULL && man == NULL) { | | 879 | if (mdoc == NULL && man == NULL) { |
880 | if (mflags.verbosity == 2) | | 880 | if (mflags.verbosity == 2) |
881 | warnx("Not a man(7) or mdoc(7) page"); | | 881 | warnx("Not a man(7) or mdoc(7) page"); |
882 | return; | | 882 | return; |
883 | } | | 883 | } |
884 | | | 884 | |
885 | set_machine(mdoc, rec); | | 885 | set_machine(mdoc, rec); |
886 | set_section(mdoc, man, rec); | | 886 | set_section(mdoc, man, rec); |
887 | if (mdoc) { | | 887 | if (mdoc) { |
888 | rec->page_type = MDOC; | | 888 | rec->page_type = MDOC; |
889 | pmdoc_node(mdoc_node(mdoc), rec); | | 889 | pmdoc_node(mdoc_node(mdoc), rec); |
890 | } else { | | 890 | } else { |
891 | rec->page_type = MAN; | | 891 | rec->page_type = MAN; |
892 | pman_node(man_node(man), rec); | | 892 | pman_node(man_node(man), rec); |
893 | } | | 893 | } |
894 | } | | 894 | } |
895 | | | 895 | |
896 | /* | | 896 | /* |
897 | * set_section -- | | 897 | * set_section -- |
898 | * Extracts the section number and normalizes it to only the numeric part | | 898 | * Extracts the section number and normalizes it to only the numeric part |
899 | * (Which should be the first character of the string). | | 899 | * (Which should be the first character of the string). |
900 | */ | | 900 | */ |
901 | static void | | 901 | static void |
902 | set_section(const struct mdoc *md, const struct man *m, mandb_rec *rec) | | 902 | set_section(const struct mdoc *md, const struct man *m, mandb_rec *rec) |
903 | { | | 903 | { |
904 | if (md) { | | 904 | if (md) { |
905 | const struct mdoc_meta *md_meta = mdoc_meta(md); | | 905 | const struct mdoc_meta *md_meta = mdoc_meta(md); |
906 | rec->section[0] = md_meta->msec[0]; | | 906 | rec->section[0] = md_meta->msec[0]; |
907 | } else if (m) { | | 907 | } else if (m) { |
908 | const struct man_meta *m_meta = man_meta(m); | | 908 | const struct man_meta *m_meta = man_meta(m); |
909 | rec->section[0] = m_meta->msec[0]; | | 909 | rec->section[0] = m_meta->msec[0]; |
910 | } | | 910 | } |
911 | } | | 911 | } |
912 | | | 912 | |
913 | /* | | 913 | /* |
914 | * get_machine -- | | 914 | * get_machine -- |
915 | * Extracts the machine architecture information if available. | | 915 | * Extracts the machine architecture information if available. |
916 | */ | | 916 | */ |
917 | static void | | 917 | static void |
918 | set_machine(const struct mdoc *md, mandb_rec *rec) | | 918 | set_machine(const struct mdoc *md, mandb_rec *rec) |
919 | { | | 919 | { |
920 | if (md == NULL) | | 920 | if (md == NULL) |
921 | return; | | 921 | return; |
922 | const struct mdoc_meta *md_meta = mdoc_meta(md); | | 922 | const struct mdoc_meta *md_meta = mdoc_meta(md); |
923 | if (md_meta->arch) | | 923 | if (md_meta->arch) |
924 | rec->machine = estrdup(md_meta->arch); | | 924 | rec->machine = estrdup(md_meta->arch); |
925 | } | | 925 | } |
926 | | | 926 | |
927 | static void | | 927 | static void |
928 | pmdoc_node(const struct mdoc_node *n, mandb_rec *rec) | | 928 | pmdoc_node(const struct mdoc_node *n, mandb_rec *rec) |
929 | { | | 929 | { |
930 | | | 930 | |
931 | if (n == NULL) | | 931 | if (n == NULL) |
932 | return; | | 932 | return; |
933 | | | 933 | |
934 | switch (n->type) { | | 934 | switch (n->type) { |
935 | case (MDOC_BODY): | | 935 | case (MDOC_BODY): |
936 | /* FALLTHROUGH */ | | 936 | /* FALLTHROUGH */ |
937 | case (MDOC_TAIL): | | 937 | case (MDOC_TAIL): |
938 | /* FALLTHROUGH */ | | 938 | /* FALLTHROUGH */ |
939 | case (MDOC_ELEM): | | 939 | case (MDOC_ELEM): |
940 | if (mdocs[n->tok] == NULL) | | 940 | if (mdocs[n->tok] == NULL) |
941 | break; | | 941 | break; |
942 | (*mdocs[n->tok])(n, rec); | | 942 | (*mdocs[n->tok])(n, rec); |
943 | break; | | 943 | break; |
944 | default: | | 944 | default: |
945 | break; | | 945 | break; |
946 | } | | 946 | } |
947 | | | 947 | |
948 | pmdoc_node(n->child, rec); | | 948 | pmdoc_node(n->child, rec); |
949 | pmdoc_node(n->next, rec); | | 949 | pmdoc_node(n->next, rec); |
950 | } | | 950 | } |
951 | | | 951 | |
952 | /* | | 952 | /* |
953 | * pmdoc_Nm -- | | 953 | * pmdoc_Nm -- |
954 | * Extracts the Name of the manual page from the .Nm macro | | 954 | * Extracts the Name of the manual page from the .Nm macro |
955 | */ | | 955 | */ |
956 | static void | | 956 | static void |
957 | pmdoc_Nm(const struct mdoc_node *n, mandb_rec *rec) | | 957 | pmdoc_Nm(const struct mdoc_node *n, mandb_rec *rec) |
958 | { | | 958 | { |
959 | if (n->sec != SEC_NAME) | | 959 | if (n->sec != SEC_NAME) |
960 | return; | | 960 | return; |
961 | | | 961 | |
962 | for (n = n->child; n; n = n->next) { | | 962 | for (n = n->child; n; n = n->next) { |
963 | if (n->type == MDOC_TEXT) { | | 963 | if (n->type == MDOC_TEXT) { |
964 | concat(&rec->name, n->string); | | 964 | concat(&rec->name, n->string); |
965 | } | | 965 | } |
966 | } | | 966 | } |
967 | } | | 967 | } |
968 | | | 968 | |
969 | /* | | 969 | /* |
970 | * pmdoc_Nd -- | | 970 | * pmdoc_Nd -- |
971 | * Extracts the one line description of the man page from the .Nd macro | | 971 | * Extracts the one line description of the man page from the .Nd macro |
972 | */ | | 972 | */ |
973 | static void | | 973 | static void |
974 | pmdoc_Nd(const struct mdoc_node *n, mandb_rec *rec) | | 974 | pmdoc_Nd(const struct mdoc_node *n, mandb_rec *rec) |
975 | { | | 975 | { |
976 | /* | | 976 | /* |
977 | * A static variable for keeping track of whether a Xr macro was seen | | 977 | * A static variable for keeping track of whether a Xr macro was seen |
978 | * previously. | | 978 | * previously. |
979 | */ | | 979 | */ |
980 | char *buf = NULL; | | 980 | char *buf = NULL; |
981 | char *temp; | | 981 | char *temp; |
982 | char *nd_text; | | 982 | char *nd_text; |
983 | | | 983 | |
984 | if (n == NULL || n->tok == MDOC_MAX) | | 984 | if (n == NULL || n->tok == MDOC_MAX) |
985 | return; | | 985 | return; |
986 | | | 986 | |
987 | if (n->type == MDOC_TEXT) { | | 987 | if (n->type == MDOC_TEXT) { |
988 | if (rec->xr_found && n->next) { | | 988 | if (rec->xr_found && n->next) { |
989 | /* | | 989 | /* |
990 | * An Xr macro was seen previously, so parse this | | 990 | * An Xr macro was seen previously, so parse this |
991 | * and the next node. | | 991 | * and the next node. |
992 | */ | | 992 | */ |
993 | temp = estrdup(n->string); | | 993 | temp = n->string; |
994 | n = n->next; | | 994 | n = n->next; |
995 | easprintf(&buf, "%s(%s)", temp, n->string); | | 995 | easprintf(&buf, "%s(%s)", temp, n->string); |
996 | concat(&rec->name_desc, buf); | | 996 | concat(&rec->name_desc, buf); |
997 | free(buf); | | 997 | free(buf); |
998 | free(temp); | | | |
999 | } else { | | 998 | } else { |
1000 | nd_text = estrdup(n->string); | | 999 | nd_text = estrdup(n->string); |
1001 | replace_hyph(nd_text); | | 1000 | replace_hyph(nd_text); |
1002 | concat(&rec->name_desc, nd_text); | | 1001 | concat(&rec->name_desc, nd_text); |
1003 | free(nd_text); | | 1002 | free(nd_text); |
1004 | } | | 1003 | } |
1005 | rec->xr_found = 0; | | 1004 | rec->xr_found = 0; |
1006 | } else if (mdocs[n->tok] == pmdoc_Xr) { | | 1005 | } else if (mdocs[n->tok] == pmdoc_Xr) { |
1007 | /* Remember that we have encountered an Xr macro */ | | 1006 | /* Remember that we have encountered an Xr macro */ |
1008 | rec->xr_found = 1; | | 1007 | rec->xr_found = 1; |
1009 | } | | 1008 | } |
1010 | | | 1009 | |
1011 | if (n->child) | | 1010 | if (n->child) |
1012 | pmdoc_Nd(n->child, rec); | | 1011 | pmdoc_Nd(n->child, rec); |
1013 | | | 1012 | |
1014 | if(n->next) | | 1013 | if(n->next) |
1015 | pmdoc_Nd(n->next, rec); | | 1014 | pmdoc_Nd(n->next, rec); |
1016 | } | | 1015 | } |
1017 | | | 1016 | |
1018 | /* | | 1017 | /* |
1019 | * pmdoc_macro_handler-- | | 1018 | * pmdoc_macro_handler-- |
1020 | * This function is a single point of handling all the special macros that we | | 1019 | * This function is a single point of handling all the special macros that we |
1021 | * want to handle especially. For example the .Xr macro for properly parsing | | 1020 | * want to handle especially. For example the .Xr macro for properly parsing |
1022 | * the referenced page name along with the section number, or the .Pp macro | | 1021 | * the referenced page name along with the section number, or the .Pp macro |
1023 | * for adding a new line whenever we encounter it. | | 1022 | * for adding a new line whenever we encounter it. |
1024 | */ | | 1023 | */ |
1025 | static void | | 1024 | static void |
1026 | pmdoc_macro_handler(const struct mdoc_node *n, mandb_rec *rec, enum mdoct doct) | | 1025 | pmdoc_macro_handler(const struct mdoc_node *n, mandb_rec *rec, enum mdoct doct) |
1027 | { | | 1026 | { |
1028 | const struct mdoc_node *sn; | | 1027 | const struct mdoc_node *sn; |
1029 | assert(n); | | 1028 | assert(n); |
1030 | | | 1029 | |
1031 | switch (doct) { | | 1030 | switch (doct) { |
1032 | /* Parse the man page references. | | 1031 | /* Parse the man page references. |
1033 | * Basically the .Xr macros are used like: | | 1032 | * Basically the .Xr macros are used like: |
1034 | * .Xr ls 1 | | 1033 | * .Xr ls 1 |
1035 | * and formatted like this: | | 1034 | * and formatted like this: |
1036 | * ls(1) | | 1035 | * ls(1) |
1037 | * Prepare a buffer to format the data like the above example and call | | 1036 | * Prepare a buffer to format the data like the above example and call |
1038 | * pmdoc_parse_section to append it. | | 1037 | * pmdoc_parse_section to append it. |
1039 | */ | | 1038 | */ |
1040 | case MDOC_Xr: | | 1039 | case MDOC_Xr: |
1041 | n = n->child; | | 1040 | n = n->child; |
1042 | while (n->type != MDOC_TEXT && n->next) | | 1041 | while (n->type != MDOC_TEXT && n->next) |
1043 | n = n->next; | | 1042 | n = n->next; |
1044 | | | 1043 | |
1045 | if (n && n->type != MDOC_TEXT) | | 1044 | if (n && n->type != MDOC_TEXT) |
1046 | return; | | 1045 | return; |
1047 | sn = n; | | 1046 | sn = n; |
1048 | if (n->next) | | 1047 | if (n->next) |
1049 | n = n->next; | | 1048 | n = n->next; |
1050 | | | 1049 | |
1051 | while (n->type != MDOC_TEXT && n->next) | | 1050 | while (n->type != MDOC_TEXT && n->next) |
1052 | n = n->next; | | 1051 | n = n->next; |
1053 | | | 1052 | |
1054 | if (n && n->type == MDOC_TEXT) { | | 1053 | if (n && n->type == MDOC_TEXT) { |
1055 | size_t len = strlen(sn->string); | | 1054 | char *buf; |
1056 | char *buf = emalloc(len + 4); | | 1055 | easprintf(&buf, "%s(%s)", sn->string, n->string); |
1057 | memcpy(buf, sn->string, len); | | | |
1058 | buf[len] = '('; | | | |
1059 | buf[len + 1] = n->string[0]; | | | |
1060 | buf[len + 2] = ')'; | | | |
1061 | buf[len + 3] = 0; | | | |
1062 | mdoc_parse_section(n->sec, buf, rec); | | 1056 | mdoc_parse_section(n->sec, buf, rec); |
1063 | free(buf); | | 1057 | free(buf); |
1064 | } | | 1058 | } |
1065 | | | 1059 | |
1066 | break; | | 1060 | break; |
1067 | | | 1061 | |
1068 | /* Parse the .Pp macro to add a new line */ | | 1062 | /* Parse the .Pp macro to add a new line */ |
1069 | case MDOC_Pp: | | 1063 | case MDOC_Pp: |
1070 | if (n->type == MDOC_TEXT) | | 1064 | if (n->type == MDOC_TEXT) |
1071 | mdoc_parse_section(n->sec, "\n", rec); | | 1065 | mdoc_parse_section(n->sec, "\n", rec); |
1072 | break; | | 1066 | break; |
1073 | default: | | 1067 | default: |
1074 | break; | | 1068 | break; |
1075 | } | | 1069 | } |
1076 | | | 1070 | |
1077 | } | | 1071 | } |
1078 | | | 1072 | |
1079 | /* | | 1073 | /* |
1080 | * pmdoc_Xr, pmdoc_Pp-- | | 1074 | * pmdoc_Xr, pmdoc_Pp-- |
1081 | * Empty stubs. | | 1075 | * Empty stubs. |
1082 | * The parser calls these functions each time it encounters | | 1076 | * The parser calls these functions each time it encounters |
1083 | * a .Xr or .Pp macro. We are parsing all the data from | | 1077 | * a .Xr or .Pp macro. We are parsing all the data from |
1084 | * the pmdoc_Sh function, so don't do anything here. | | 1078 | * the pmdoc_Sh function, so don't do anything here. |
1085 | * (See if else blocks in pmdoc_Sh.) | | 1079 | * (See if else blocks in pmdoc_Sh.) |
1086 | */ | | 1080 | */ |
1087 | static void | | 1081 | static void |
1088 | pmdoc_Xr(const struct mdoc_node *n, mandb_rec *rec) | | 1082 | pmdoc_Xr(const struct mdoc_node *n, mandb_rec *rec) |
1089 | { | | 1083 | { |
1090 | } | | 1084 | } |
1091 | | | 1085 | |
1092 | static void | | 1086 | static void |
1093 | pmdoc_Pp(const struct mdoc_node *n, mandb_rec *rec) | | 1087 | pmdoc_Pp(const struct mdoc_node *n, mandb_rec *rec) |
1094 | { | | 1088 | { |
1095 | } | | 1089 | } |
1096 | | | 1090 | |
1097 | /* | | 1091 | /* |
1098 | * pmdoc_Sh -- | | 1092 | * pmdoc_Sh -- |
1099 | * Called when a .Sh macro is encountered and loops through its body, calling | | 1093 | * Called when a .Sh macro is encountered and loops through its body, calling |
1100 | * mdoc_parse_section to append the data to the section specific buffer. | | 1094 | * mdoc_parse_section to append the data to the section specific buffer. |
1101 | * Two special macros which may occur inside the body of Sh are .Nm and .Xr and | | 1095 | * Two special macros which may occur inside the body of Sh are .Nm and .Xr and |
1102 | * they need special handling, thus the separate if branches for them. | | 1096 | * they need special handling, thus the separate if branches for them. |
1103 | */ | | 1097 | */ |
1104 | static void | | 1098 | static void |
1105 | pmdoc_Sh(const struct mdoc_node *n, mandb_rec *rec) | | 1099 | pmdoc_Sh(const struct mdoc_node *n, mandb_rec *rec) |
1106 | { | | 1100 | { |
1107 | if (n == NULL || n->tok == MDOC_MAX) | | 1101 | if (n == NULL || n->tok == MDOC_MAX) |
1108 | return; | | 1102 | return; |
1109 | int xr_found = 0; | | 1103 | int xr_found = 0; |
1110 | | | 1104 | |
1111 | if (n->type == MDOC_TEXT) { | | 1105 | if (n->type == MDOC_TEXT) { |
1112 | mdoc_parse_section(n->sec, n->string, rec); | | 1106 | mdoc_parse_section(n->sec, n->string, rec); |
1113 | } else if (mdocs[n->tok] == pmdoc_Nm && rec->name != NULL) { | | 1107 | } else if (mdocs[n->tok] == pmdoc_Nm && rec->name != NULL) { |
1114 | /* | | 1108 | /* |
1115 | * When encountering a .Nm macro, substitute it | | 1109 | * When encountering a .Nm macro, substitute it |
1116 | * with its previously cached value of the argument. | | 1110 | * with its previously cached value of the argument. |
1117 | */ | | 1111 | */ |
1118 | mdoc_parse_section(n->sec, rec->name, rec); | | 1112 | mdoc_parse_section(n->sec, rec->name, rec); |
1119 | } else if (mdocs[n->tok] == pmdoc_Xr) { | | 1113 | } else if (mdocs[n->tok] == pmdoc_Xr) { |
1120 | /* | | 1114 | /* |
1121 | * When encountering other inline macros, | | 1115 | * When encountering other inline macros, |
1122 | * call pmdoc_macro_handler. | | 1116 | * call pmdoc_macro_handler. |
1123 | */ | | 1117 | */ |
1124 | pmdoc_macro_handler(n, rec, MDOC_Xr); | | 1118 | pmdoc_macro_handler(n, rec, MDOC_Xr); |
1125 | xr_found = 1; | | 1119 | xr_found = 1; |
1126 | } else if (mdocs[n->tok] == pmdoc_Pp) { | | 1120 | } else if (mdocs[n->tok] == pmdoc_Pp) { |
1127 | pmdoc_macro_handler(n, rec, MDOC_Pp); | | 1121 | pmdoc_macro_handler(n, rec, MDOC_Pp); |
1128 | } | | 1122 | } |
1129 | | | 1123 | |
1130 | /* | | 1124 | /* |
1131 | * If an Xr macro was encountered then the child node has | | 1125 | * If an Xr macro was encountered then the child node has |
1132 | * already been explored by pmdoc_macro_handler. | | 1126 | * already been explored by pmdoc_macro_handler. |
1133 | */ | | 1127 | */ |
1134 | if (xr_found == 0) | | 1128 | if (xr_found == 0) |
1135 | pmdoc_Sh(n->child, rec); | | 1129 | pmdoc_Sh(n->child, rec); |
1136 | pmdoc_Sh(n->next, rec); | | 1130 | pmdoc_Sh(n->next, rec); |
1137 | } | | 1131 | } |
1138 | | | 1132 | |
1139 | /* | | 1133 | /* |
1140 | * mdoc_parse_section-- | | 1134 | * mdoc_parse_section-- |
1141 | * Utility function for parsing sections of the mdoc type pages. | | 1135 | * Utility function for parsing sections of the mdoc type pages. |
1142 | * Takes two params: | | 1136 | * Takes two params: |
1143 | * 1. sec is an enum which indicates the section in which we are present | | 1137 | * 1. sec is an enum which indicates the section in which we are present |
1144 | * 2. string is the string which we need to append to the secbuff for this | | 1138 | * 2. string is the string which we need to append to the secbuff for this |
1145 | * particular section. | | 1139 | * particular section. |
1146 | * The function appends string to the global section buffer and returns. | | 1140 | * The function appends string to the global section buffer and returns. |
1147 | */ | | 1141 | */ |
1148 | static void | | 1142 | static void |
1149 | mdoc_parse_section(enum mdoc_sec sec, const char *string, mandb_rec *rec) | | 1143 | mdoc_parse_section(enum mdoc_sec sec, const char *string, mandb_rec *rec) |
1150 | { | | 1144 | { |
1151 | /* | | 1145 | /* |
1152 | * If the user specified the 'l' flag, then parse and store only the | | 1146 | * If the user specified the 'l' flag, then parse and store only the |
1153 | * NAME section. Ignore the rest. | | 1147 | * NAME section. Ignore the rest. |
1154 | */ | | 1148 | */ |
1155 | if (mflags.limit) | | 1149 | if (mflags.limit) |
1156 | return; | | 1150 | return; |
1157 | | | 1151 | |
1158 | switch (sec) { | | 1152 | switch (sec) { |
1159 | case SEC_LIBRARY: | | 1153 | case SEC_LIBRARY: |
1160 | append(&rec->lib, string); | | 1154 | append(&rec->lib, string); |
1161 | break; | | 1155 | break; |
1162 | case SEC_RETURN_VALUES: | | 1156 | case SEC_RETURN_VALUES: |
1163 | append(&rec->return_vals, string); | | 1157 | append(&rec->return_vals, string); |
1164 | break; | | 1158 | break; |
1165 | case SEC_ENVIRONMENT: | | 1159 | case SEC_ENVIRONMENT: |
1166 | append(&rec->env, string); | | 1160 | append(&rec->env, string); |
1167 | break; | | 1161 | break; |
1168 | case SEC_FILES: | | 1162 | case SEC_FILES: |
1169 | append(&rec->files, string); | | 1163 | append(&rec->files, string); |
1170 | break; | | 1164 | break; |
1171 | case SEC_EXIT_STATUS: | | 1165 | case SEC_EXIT_STATUS: |
1172 | append(&rec->exit_status, string); | | 1166 | append(&rec->exit_status, string); |
1173 | break; | | 1167 | break; |
1174 | case SEC_DIAGNOSTICS: | | 1168 | case SEC_DIAGNOSTICS: |
1175 | append(&rec->diagnostics, string); | | 1169 | append(&rec->diagnostics, string); |
1176 | break; | | 1170 | break; |
1177 | case SEC_ERRORS: | | 1171 | case SEC_ERRORS: |
1178 | append(&rec->errors, string); | | 1172 | append(&rec->errors, string); |
1179 | break; | | 1173 | break; |
1180 | case SEC_NAME: | | 1174 | case SEC_NAME: |
1181 | case SEC_SYNOPSIS: | | 1175 | case SEC_SYNOPSIS: |
1182 | case SEC_EXAMPLES: | | 1176 | case SEC_EXAMPLES: |
1183 | case SEC_STANDARDS: | | 1177 | case SEC_STANDARDS: |
1184 | case SEC_HISTORY: | | 1178 | case SEC_HISTORY: |
1185 | case SEC_AUTHORS: | | 1179 | case SEC_AUTHORS: |
1186 | case SEC_BUGS: | | 1180 | case SEC_BUGS: |
1187 | break; | | 1181 | break; |
1188 | default: | | 1182 | default: |
1189 | append(&rec->desc, string); | | 1183 | append(&rec->desc, string); |
1190 | break; | | 1184 | break; |
1191 | } | | 1185 | } |
1192 | } | | 1186 | } |
1193 | | | 1187 | |
1194 | static void | | 1188 | static void |
1195 | pman_node(const struct man_node *n, mandb_rec *rec) | | 1189 | pman_node(const struct man_node *n, mandb_rec *rec) |
1196 | { | | 1190 | { |
1197 | if (n == NULL) | | 1191 | if (n == NULL) |
1198 | return; | | 1192 | return; |
1199 | | | 1193 | |
1200 | switch (n->type) { | | 1194 | switch (n->type) { |
1201 | case (MAN_BODY): | | 1195 | case (MAN_BODY): |
1202 | /* FALLTHROUGH */ | | 1196 | /* FALLTHROUGH */ |
1203 | case (MAN_TAIL): | | 1197 | case (MAN_TAIL): |
1204 | /* FALLTHROUGH */ | | 1198 | /* FALLTHROUGH */ |
1205 | case (MAN_BLOCK): | | 1199 | case (MAN_BLOCK): |
1206 | /* FALLTHROUGH */ | | 1200 | /* FALLTHROUGH */ |
1207 | case (MAN_ELEM): | | 1201 | case (MAN_ELEM): |
1208 | if (mans[n->tok] != NULL) | | 1202 | if (mans[n->tok] != NULL) |
1209 | (*mans[n->tok])(n, rec); | | 1203 | (*mans[n->tok])(n, rec); |
1210 | break; | | 1204 | break; |
1211 | default: | | 1205 | default: |
1212 | break; | | 1206 | break; |
1213 | } | | 1207 | } |
1214 | | | 1208 | |
1215 | pman_node(n->child, rec); | | 1209 | pman_node(n->child, rec); |
1216 | pman_node(n->next, rec); | | 1210 | pman_node(n->next, rec); |
1217 | } | | 1211 | } |
1218 | | | 1212 | |
1219 | /* | | 1213 | /* |
1220 | * pman_parse_name -- | | 1214 | * pman_parse_name -- |
1221 | * Parses the NAME section and puts the complete content in the name_desc | | 1215 | * Parses the NAME section and puts the complete content in the name_desc |
1222 | * variable. | | 1216 | * variable. |
1223 | */ | | 1217 | */ |
1224 | static void | | 1218 | static void |
1225 | pman_parse_name(const struct man_node *n, mandb_rec *rec) | | 1219 | pman_parse_name(const struct man_node *n, mandb_rec *rec) |
1226 | { | | 1220 | { |
1227 | if (n == NULL) | | 1221 | if (n == NULL) |
1228 | return; | | 1222 | return; |
1229 | | | 1223 | |
1230 | if (n->type == MAN_TEXT) { | | 1224 | if (n->type == MAN_TEXT) { |
1231 | char *tmp = parse_escape(n->string); | | 1225 | char *tmp = parse_escape(n->string); |
1232 | concat(&rec->name_desc, tmp); | | 1226 | concat(&rec->name_desc, tmp); |
1233 | free(tmp); | | 1227 | free(tmp); |
1234 | } | | 1228 | } |
1235 | | | 1229 | |
1236 | if (n->child) | | 1230 | if (n->child) |
1237 | pman_parse_name(n->child, rec); | | 1231 | pman_parse_name(n->child, rec); |
1238 | | | 1232 | |
1239 | if(n->next) | | 1233 | if(n->next) |
1240 | pman_parse_name(n->next, rec); | | 1234 | pman_parse_name(n->next, rec); |
1241 | } | | 1235 | } |
1242 | | | 1236 | |
1243 | /* | | 1237 | /* |
1244 | * A stub function to be able to parse the macros like .B embedded inside | | 1238 | * A stub function to be able to parse the macros like .B embedded inside |
1245 | * a section. | | 1239 | * a section. |
1246 | */ | | 1240 | */ |
1247 | static void | | 1241 | static void |
1248 | pman_block(const struct man_node *n, mandb_rec *rec) | | 1242 | pman_block(const struct man_node *n, mandb_rec *rec) |
1249 | { | | 1243 | { |
1250 | } | | 1244 | } |
1251 | | | 1245 | |
1252 | /* | | 1246 | /* |
1253 | * pman_sh -- | | 1247 | * pman_sh -- |
1254 | * This function does one of the two things: | | 1248 | * This function does one of the two things: |
1255 | * 1. If the present section is NAME, then it will: | | 1249 | * 1. If the present section is NAME, then it will: |
1256 | * (a) Extract the name of the page (in case of multiple comma separated | | 1250 | * (a) Extract the name of the page (in case of multiple comma separated |
1257 | * names, it will pick up the first one). | | 1251 | * names, it will pick up the first one). |
1258 | * (b) Build a space spearated list of all the symlinks/hardlinks to | | 1252 | * (b) Build a space spearated list of all the symlinks/hardlinks to |
1259 | * this page and store in the buffer 'links'. These are extracted from | | 1253 | * this page and store in the buffer 'links'. These are extracted from |
1260 | * the comma separated list of names in the NAME section as well. | | 1254 | * the comma separated list of names in the NAME section as well. |
1261 | * (c) Move on to the one line description section, which is after the list | | 1255 | * (c) Move on to the one line description section, which is after the list |
1262 | * of names in the NAME section. | | 1256 | * of names in the NAME section. |
1263 | * 2. Otherwise, it will check the section name and call the man_parse_section | | 1257 | * 2. Otherwise, it will check the section name and call the man_parse_section |
1264 | * function, passing the enum corresponding that section. | | 1258 | * function, passing the enum corresponding that section. |
1265 | */ | | 1259 | */ |
1266 | static void | | 1260 | static void |
1267 | pman_sh(const struct man_node *n, mandb_rec *rec) | | 1261 | pman_sh(const struct man_node *n, mandb_rec *rec) |
1268 | { | | 1262 | { |
1269 | static const struct { | | 1263 | static const struct { |
1270 | enum man_sec section; | | 1264 | enum man_sec section; |
1271 | const char *header; | | 1265 | const char *header; |
1272 | } mapping[] = { | | 1266 | } mapping[] = { |
1273 | { MANSEC_DESCRIPTION, "DESCRIPTION" }, | | 1267 | { MANSEC_DESCRIPTION, "DESCRIPTION" }, |
1274 | { MANSEC_SYNOPSIS, "SYNOPSIS" }, | | 1268 | { MANSEC_SYNOPSIS, "SYNOPSIS" }, |
1275 | { MANSEC_LIBRARY, "LIBRARY" }, | | 1269 | { MANSEC_LIBRARY, "LIBRARY" }, |
1276 | { MANSEC_ERRORS, "ERRORS" }, | | 1270 | { MANSEC_ERRORS, "ERRORS" }, |
1277 | { MANSEC_FILES, "FILES" }, | | 1271 | { MANSEC_FILES, "FILES" }, |
1278 | { MANSEC_RETURN_VALUES, "RETURN VALUE" }, | | 1272 | { MANSEC_RETURN_VALUES, "RETURN VALUE" }, |
1279 | { MANSEC_RETURN_VALUES, "RETURN VALUES" }, | | 1273 | { MANSEC_RETURN_VALUES, "RETURN VALUES" }, |
1280 | { MANSEC_EXIT_STATUS, "EXIT STATUS" }, | | 1274 | { MANSEC_EXIT_STATUS, "EXIT STATUS" }, |
1281 | { MANSEC_EXAMPLES, "EXAMPLES" }, | | 1275 | { MANSEC_EXAMPLES, "EXAMPLES" }, |
1282 | { MANSEC_EXAMPLES, "EXAMPLE" }, | | 1276 | { MANSEC_EXAMPLES, "EXAMPLE" }, |
1283 | { MANSEC_STANDARDS, "STANDARDS" }, | | 1277 | { MANSEC_STANDARDS, "STANDARDS" }, |
1284 | { MANSEC_HISTORY, "HISTORY" }, | | 1278 | { MANSEC_HISTORY, "HISTORY" }, |
1285 | { MANSEC_BUGS, "BUGS" }, | | 1279 | { MANSEC_BUGS, "BUGS" }, |
1286 | { MANSEC_AUTHORS, "AUTHORS" }, | | 1280 | { MANSEC_AUTHORS, "AUTHORS" }, |
1287 | { MANSEC_COPYRIGHT, "COPYRIGHT" }, | | 1281 | { MANSEC_COPYRIGHT, "COPYRIGHT" }, |
1288 | }; | | 1282 | }; |
1289 | const struct man_node *head; | | 1283 | const struct man_node *head; |
1290 | char *name_desc; | | 1284 | char *name_desc; |
1291 | int sz; | | 1285 | int sz; |
1292 | size_t i; | | 1286 | size_t i; |
1293 | | | 1287 | |
1294 | if ((head = n->parent->head) == NULL || (head = head->child) == NULL || | | 1288 | if ((head = n->parent->head) == NULL || (head = head->child) == NULL || |
1295 | head->type != MAN_TEXT) | | 1289 | head->type != MAN_TEXT) |
1296 | return; | | 1290 | return; |
1297 | | | 1291 | |
1298 | /* | | 1292 | /* |
1299 | * Check if this section should be extracted and | | 1293 | * Check if this section should be extracted and |
1300 | * where it should be stored. Handled the trival cases first. | | 1294 | * where it should be stored. Handled the trival cases first. |
1301 | */ | | 1295 | */ |
1302 | for (i = 0; i < sizeof(mapping) / sizeof(mapping[0]); ++i) { | | 1296 | for (i = 0; i < sizeof(mapping) / sizeof(mapping[0]); ++i) { |
1303 | if (strcmp(head->string, mapping[i].header) == 0) { | | 1297 | if (strcmp(head->string, mapping[i].header) == 0) { |
1304 | man_parse_section(mapping[i].section, n, rec); | | 1298 | man_parse_section(mapping[i].section, n, rec); |
1305 | return; | | 1299 | return; |
1306 | } | | 1300 | } |
1307 | } | | 1301 | } |
1308 | | | 1302 | |
1309 | if (strcmp(head->string, "NAME") == 0) { | | 1303 | if (strcmp(head->string, "NAME") == 0) { |
1310 | /* | | 1304 | /* |
1311 | * We are in the NAME section. | | 1305 | * We are in the NAME section. |
1312 | * pman_parse_name will put the complete content in name_desc. | | 1306 | * pman_parse_name will put the complete content in name_desc. |
1313 | */ | | 1307 | */ |
1314 | pman_parse_name(n, rec); | | 1308 | pman_parse_name(n, rec); |
1315 | | | 1309 | |
1316 | name_desc = rec->name_desc; | | 1310 | name_desc = rec->name_desc; |
1317 | if (name_desc == NULL) | | 1311 | if (name_desc == NULL) |
1318 | return; | | 1312 | return; |
1319 | | | 1313 | |
1320 | /* Remove any leading spaces. */ | | 1314 | /* Remove any leading spaces. */ |
1321 | while (name_desc[0] == ' ') | | 1315 | while (name_desc[0] == ' ') |
1322 | name_desc++; | | 1316 | name_desc++; |
1323 | | | 1317 | |
1324 | /* If the line begins with a "\&", avoid those */ | | 1318 | /* If the line begins with a "\&", avoid those */ |
1325 | if (name_desc[0] == '\\' && name_desc[1] == '&') | | 1319 | if (name_desc[0] == '\\' && name_desc[1] == '&') |
1326 | name_desc += 2; | | 1320 | name_desc += 2; |
1327 | | | 1321 | |
1328 | /* Now name_desc should be left with a comma-space | | 1322 | /* Now name_desc should be left with a comma-space |
1329 | * separated list of names and the one line description | | 1323 | * separated list of names and the one line description |
1330 | * of the page: | | 1324 | * of the page: |
1331 | * "a, b, c \- sample description" | | 1325 | * "a, b, c \- sample description" |
1332 | * Take out the first name, before the first comma | | 1326 | * Take out the first name, before the first comma |
1333 | * (or space) and store it in rec->name. | | 1327 | * (or space) and store it in rec->name. |
1334 | * If the page has aliases then they should be | | 1328 | * If the page has aliases then they should be |
1335 | * in the form of a comma separated list. | | 1329 | * in the form of a comma separated list. |
1336 | * Keep looping while there is a comma in name_desc, | | 1330 | * Keep looping while there is a comma in name_desc, |
1337 | * extract the alias name and store in rec->links. | | 1331 | * extract the alias name and store in rec->links. |
1338 | * When there are no more commas left, break out. | | 1332 | * When there are no more commas left, break out. |
1339 | */ | | 1333 | */ |
1340 | int has_alias = 0; // Any more aliases left? | | 1334 | int has_alias = 0; // Any more aliases left? |
1341 | while (*name_desc) { | | 1335 | while (*name_desc) { |
1342 | /* Remove any leading spaces or hyphens. */ | | 1336 | /* Remove any leading spaces or hyphens. */ |
1343 | if (name_desc[0] == ' ' || name_desc[0] =='-') { | | 1337 | if (name_desc[0] == ' ' || name_desc[0] =='-') { |
1344 | name_desc++; | | 1338 | name_desc++; |
1345 | continue; | | 1339 | continue; |
1346 | } | | 1340 | } |
1347 | sz = strcspn(name_desc, ", "); | | 1341 | sz = strcspn(name_desc, ", "); |
1348 | | | 1342 | |
1349 | /* Extract the first term and store it in rec->name. */ | | 1343 | /* Extract the first term and store it in rec->name. */ |
1350 | if (rec->name == NULL) { | | 1344 | if (rec->name == NULL) { |
1351 | if (name_desc[sz] == ',') | | 1345 | if (name_desc[sz] == ',') |
1352 | has_alias = 1; | | 1346 | has_alias = 1; |
1353 | name_desc[sz] = 0; | | 1347 | name_desc[sz] = 0; |
1354 | rec->name = emalloc(sz + 1); | | 1348 | rec->name = emalloc(sz + 1); |
1355 | memcpy(rec->name, name_desc, sz + 1); | | 1349 | memcpy(rec->name, name_desc, sz + 1); |
1356 | name_desc += sz + 1; | | 1350 | name_desc += sz + 1; |
1357 | continue; | | 1351 | continue; |
1358 | } | | 1352 | } |
1359 | | | 1353 | |
1360 | /* | | 1354 | /* |
1361 | * Once rec->name is set, rest of the names | | 1355 | * Once rec->name is set, rest of the names |
1362 | * are to be treated as links or aliases. | | 1356 | * are to be treated as links or aliases. |
1363 | */ | | 1357 | */ |
1364 | if (rec->name && has_alias) { | | 1358 | if (rec->name && has_alias) { |
1365 | if (name_desc[sz] != ',') { | | 1359 | if (name_desc[sz] != ',') { |
1366 | /* No more commas left --> | | 1360 | /* No more commas left --> |
1367 | * no more aliases to take out | | 1361 | * no more aliases to take out |
1368 | */ | | 1362 | */ |
1369 | has_alias = 0; | | 1363 | has_alias = 0; |
1370 | } | | 1364 | } |
1371 | name_desc[sz] = 0; | | 1365 | name_desc[sz] = 0; |
1372 | concat2(&rec->links, name_desc, sz); | | 1366 | concat2(&rec->links, name_desc, sz); |
1373 | name_desc += sz + 1; | | 1367 | name_desc += sz + 1; |
1374 | continue; | | 1368 | continue; |
1375 | } | | 1369 | } |
1376 | break; | | 1370 | break; |
1377 | } | | 1371 | } |
1378 | | | 1372 | |
1379 | /* Parse any escape sequences that might be there */ | | 1373 | /* Parse any escape sequences that might be there */ |
1380 | char *temp = parse_escape(name_desc); | | 1374 | char *temp = parse_escape(name_desc); |
1381 | free(rec->name_desc); | | 1375 | free(rec->name_desc); |
1382 | rec->name_desc = temp; | | 1376 | rec->name_desc = temp; |
1383 | temp = parse_escape(rec->name); | | 1377 | temp = parse_escape(rec->name); |
1384 | free(rec->name); | | 1378 | free(rec->name); |
1385 | rec->name = temp; | | 1379 | rec->name = temp; |
1386 | return; | | 1380 | return; |
1387 | } | | 1381 | } |
1388 | | | 1382 | |
1389 | /* The RETURN VALUE section might be specified in multiple ways */ | | 1383 | /* The RETURN VALUE section might be specified in multiple ways */ |
1390 | if (strcmp(head->string, "RETURN") == 0 && | | 1384 | if (strcmp(head->string, "RETURN") == 0 && |
1391 | head->next != NULL && head->next->type == MAN_TEXT && | | 1385 | head->next != NULL && head->next->type == MAN_TEXT && |
1392 | (strcmp(head->next->string, "VALUE") == 0 || | | 1386 | (strcmp(head->next->string, "VALUE") == 0 || |
1393 | strcmp(head->next->string, "VALUES") == 0)) { | | 1387 | strcmp(head->next->string, "VALUES") == 0)) { |
1394 | man_parse_section(MANSEC_RETURN_VALUES, n, rec); | | 1388 | man_parse_section(MANSEC_RETURN_VALUES, n, rec); |
1395 | return; | | 1389 | return; |
1396 | } | | 1390 | } |
1397 | | | 1391 | |
1398 | /* | | 1392 | /* |
1399 | * EXIT STATUS section can also be specified all on one line or on two | | 1393 | * EXIT STATUS section can also be specified all on one line or on two |
1400 | * separate lines. | | 1394 | * separate lines. |
1401 | */ | | 1395 | */ |
1402 | if (strcmp(head->string, "EXIT") == 0 && | | 1396 | if (strcmp(head->string, "EXIT") == 0 && |
1403 | head->next != NULL && head->next->type == MAN_TEXT && | | 1397 | head->next != NULL && head->next->type == MAN_TEXT && |
1404 | strcmp(head->next->string, "STATUS") == 0) { | | 1398 | strcmp(head->next->string, "STATUS") == 0) { |
1405 | man_parse_section(MANSEC_EXIT_STATUS, n, rec); | | 1399 | man_parse_section(MANSEC_EXIT_STATUS, n, rec); |
1406 | return; | | 1400 | return; |
1407 | } | | 1401 | } |
1408 | | | 1402 | |
1409 | /* Store the rest of the content in desc. */ | | 1403 | /* Store the rest of the content in desc. */ |
1410 | man_parse_section(MANSEC_NONE, n, rec); | | 1404 | man_parse_section(MANSEC_NONE, n, rec); |
1411 | } | | 1405 | } |
1412 | | | 1406 | |
1413 | /* | | 1407 | /* |
1414 | * pman_parse_node -- | | 1408 | * pman_parse_node -- |
1415 | * Generic function to iterate through a node. Usually called from | | 1409 | * Generic function to iterate through a node. Usually called from |
1416 | * man_parse_section to parse a particular section of the man page. | | 1410 | * man_parse_section to parse a particular section of the man page. |
1417 | */ | | 1411 | */ |
1418 | static void | | 1412 | static void |
1419 | pman_parse_node(const struct man_node *n, secbuff *s) | | 1413 | pman_parse_node(const struct man_node *n, secbuff *s) |
1420 | { | | 1414 | { |
1421 | if (n == NULL) | | 1415 | if (n == NULL) |
1422 | return; | | 1416 | return; |
1423 | | | 1417 | |
1424 | if (n->type == MAN_TEXT) | | 1418 | if (n->type == MAN_TEXT) |
1425 | append(s, n->string); | | 1419 | append(s, n->string); |
1426 | | | 1420 | |
1427 | pman_parse_node(n->child, s); | | 1421 | pman_parse_node(n->child, s); |
1428 | pman_parse_node(n->next, s); | | 1422 | pman_parse_node(n->next, s); |
1429 | } | | 1423 | } |
1430 | | | 1424 | |
1431 | /* | | 1425 | /* |
1432 | * man_parse_section -- | | 1426 | * man_parse_section -- |
1433 | * Takes two parameters: | | 1427 | * Takes two parameters: |
1434 | * sec: Tells which section we are present in | | 1428 | * sec: Tells which section we are present in |
1435 | * n: Is the present node of the AST. | | 1429 | * n: Is the present node of the AST. |
1436 | * Depending on the section, we call pman_parse_node to parse that section and | | 1430 | * Depending on the section, we call pman_parse_node to parse that section and |
1437 | * concatenate the content from that section into the buffer for that section. | | 1431 | * concatenate the content from that section into the buffer for that section. |
1438 | */ | | 1432 | */ |
1439 | static void | | 1433 | static void |
1440 | man_parse_section(enum man_sec sec, const struct man_node *n, mandb_rec *rec) | | 1434 | man_parse_section(enum man_sec sec, const struct man_node *n, mandb_rec *rec) |
1441 | { | | 1435 | { |
1442 | /* | | 1436 | /* |
1443 | * If the user sepecified the 'l' flag then just parse | | 1437 | * If the user sepecified the 'l' flag then just parse |
1444 | * the NAME section, ignore the rest. | | 1438 | * the NAME section, ignore the rest. |
1445 | */ | | 1439 | */ |
1446 | if (mflags.limit) | | 1440 | if (mflags.limit) |
1447 | return; | | 1441 | return; |
1448 | | | 1442 | |
1449 | switch (sec) { | | 1443 | switch (sec) { |
1450 | case MANSEC_LIBRARY: | | 1444 | case MANSEC_LIBRARY: |
1451 | pman_parse_node(n, &rec->lib); | | 1445 | pman_parse_node(n, &rec->lib); |
1452 | break; | | 1446 | break; |
1453 | case MANSEC_RETURN_VALUES: | | 1447 | case MANSEC_RETURN_VALUES: |
1454 | pman_parse_node(n, &rec->return_vals); | | 1448 | pman_parse_node(n, &rec->return_vals); |
1455 | break; | | 1449 | break; |
1456 | case MANSEC_ENVIRONMENT: | | 1450 | case MANSEC_ENVIRONMENT: |
1457 | pman_parse_node(n, &rec->env); | | 1451 | pman_parse_node(n, &rec->env); |
1458 | break; | | 1452 | break; |
1459 | case MANSEC_FILES: | | 1453 | case MANSEC_FILES: |
1460 | pman_parse_node(n, &rec->files); | | 1454 | pman_parse_node(n, &rec->files); |
1461 | break; | | 1455 | break; |
1462 | case MANSEC_EXIT_STATUS: | | 1456 | case MANSEC_EXIT_STATUS: |
1463 | pman_parse_node(n, &rec->exit_status); | | 1457 | pman_parse_node(n, &rec->exit_status); |
1464 | break; | | 1458 | break; |
1465 | case MANSEC_DIAGNOSTICS: | | 1459 | case MANSEC_DIAGNOSTICS: |
1466 | pman_parse_node(n, &rec->diagnostics); | | 1460 | pman_parse_node(n, &rec->diagnostics); |
1467 | break; | | 1461 | break; |
1468 | case MANSEC_ERRORS: | | 1462 | case MANSEC_ERRORS: |
1469 | pman_parse_node(n, &rec->errors); | | 1463 | pman_parse_node(n, &rec->errors); |
1470 | break; | | 1464 | break; |
1471 | case MANSEC_NAME: | | 1465 | case MANSEC_NAME: |
1472 | case MANSEC_SYNOPSIS: | | 1466 | case MANSEC_SYNOPSIS: |
1473 | case MANSEC_EXAMPLES: | | 1467 | case MANSEC_EXAMPLES: |
1474 | case MANSEC_STANDARDS: | | 1468 | case MANSEC_STANDARDS: |
1475 | case MANSEC_HISTORY: | | 1469 | case MANSEC_HISTORY: |
1476 | case MANSEC_BUGS: | | 1470 | case MANSEC_BUGS: |
1477 | case MANSEC_AUTHORS: | | 1471 | case MANSEC_AUTHORS: |
1478 | case MANSEC_COPYRIGHT: | | 1472 | case MANSEC_COPYRIGHT: |
1479 | break; | | 1473 | break; |
1480 | default: | | 1474 | default: |
1481 | pman_parse_node(n, &rec->desc); | | 1475 | pman_parse_node(n, &rec->desc); |
1482 | break; | | 1476 | break; |
1483 | } | | 1477 | } |
1484 | | | 1478 | |
1485 | } | | 1479 | } |
1486 | | | 1480 | |
1487 | /* | | 1481 | /* |
1488 | * insert_into_db -- | | 1482 | * insert_into_db -- |
1489 | * Inserts the parsed data of the man page in the Sqlite databse. | | 1483 | * Inserts the parsed data of the man page in the Sqlite databse. |
1490 | * If any of the values is NULL, then we cleanup and return -1 indicating | | 1484 | * If any of the values is NULL, then we cleanup and return -1 indicating |
1491 | * an error. | | 1485 | * an error. |
1492 | * Otherwise, store the data in the database and return 0. | | 1486 | * Otherwise, store the data in the database and return 0. |
1493 | */ | | 1487 | */ |
1494 | static int | | 1488 | static int |
1495 | insert_into_db(sqlite3 *db, mandb_rec *rec) | | 1489 | insert_into_db(sqlite3 *db, mandb_rec *rec) |
1496 | { | | 1490 | { |
1497 | int rc = 0; | | 1491 | int rc = 0; |
1498 | int idx = -1; | | 1492 | int idx = -1; |
1499 | const char *sqlstr = NULL; | | 1493 | const char *sqlstr = NULL; |
1500 | sqlite3_stmt *stmt = NULL; | | 1494 | sqlite3_stmt *stmt = NULL; |
1501 | char *ln = NULL; | | 1495 | char *ln = NULL; |
1502 | char *errmsg = NULL; | | 1496 | char *errmsg = NULL; |
1503 | long int mandb_rowid; | | 1497 | long int mandb_rowid; |
1504 | | | 1498 | |
1505 | /* | | 1499 | /* |
1506 | * At the very minimum we want to make sure that we store | | 1500 | * At the very minimum we want to make sure that we store |
1507 | * the following data: | | 1501 | * the following data: |
1508 | * Name, one line description, and the MD5 hash | | 1502 | * Name, one line description, and the MD5 hash |
1509 | */ | | 1503 | */ |
1510 | if (rec->name == NULL || rec->name_desc == NULL || | | 1504 | if (rec->name == NULL || rec->name_desc == NULL || |
1511 | rec->md5_hash == NULL) { | | 1505 | rec->md5_hash == NULL) { |
1512 | cleanup(rec); | | 1506 | cleanup(rec); |
1513 | return -1; | | 1507 | return -1; |
1514 | } | | 1508 | } |
1515 | | | 1509 | |
1516 | /* Write null byte at the end of all the sec_buffs */ | | 1510 | /* Write null byte at the end of all the sec_buffs */ |
1517 | rec->desc.data[rec->desc.offset] = 0; | | 1511 | rec->desc.data[rec->desc.offset] = 0; |
1518 | rec->lib.data[rec->lib.offset] = 0; | | 1512 | rec->lib.data[rec->lib.offset] = 0; |
1519 | rec->env.data[rec->env.offset] = 0; | | 1513 | rec->env.data[rec->env.offset] = 0; |
1520 | rec->return_vals.data[rec->return_vals.offset] = 0; | | 1514 | rec->return_vals.data[rec->return_vals.offset] = 0; |
1521 | rec->exit_status.data[rec->exit_status.offset] = 0; | | 1515 | rec->exit_status.data[rec->exit_status.offset] = 0; |
1522 | rec->files.data[rec->files.offset] = 0; | | 1516 | rec->files.data[rec->files.offset] = 0; |
1523 | rec->diagnostics.data[rec->diagnostics.offset] = 0; | | 1517 | rec->diagnostics.data[rec->diagnostics.offset] = 0; |
1524 | rec->errors.data[rec->errors.offset] = 0; | | 1518 | rec->errors.data[rec->errors.offset] = 0; |
1525 | | | 1519 | |
1526 | /* | | 1520 | /* |
1527 | * In case of a mdoc page: (sorry, no better place to put this code) | | 1521 | * In case of a mdoc page: (sorry, no better place to put this code) |
1528 | * parse the comma separated list of names of man pages, | | 1522 | * parse the comma separated list of names of man pages, |
1529 | * the first name will be stored in the mandb table, rest will be | | 1523 | * the first name will be stored in the mandb table, rest will be |
1530 | * treated as links and put in the mandb_links table. | | 1524 | * treated as links and put in the mandb_links table. |
1531 | */ | | 1525 | */ |
1532 | if (rec->page_type == MDOC) { | | 1526 | if (rec->page_type == MDOC) { |
1533 | char *tmp; | | 1527 | char *tmp; |
1534 | rec->links = estrdup(rec->name); | | 1528 | rec->links = estrdup(rec->name); |
1535 | free(rec->name); | | 1529 | free(rec->name); |
1536 | int sz = strcspn(rec->links, " \0"); | | 1530 | int sz = strcspn(rec->links, " \0"); |
1537 | rec->name = emalloc(sz + 1); | | 1531 | rec->name = emalloc(sz + 1); |
1538 | memcpy(rec->name, rec->links, sz); | | 1532 | memcpy(rec->name, rec->links, sz); |
1539 | if(rec->name[sz - 1] == ',') | | 1533 | if(rec->name[sz - 1] == ',') |
1540 | rec->name[sz - 1] = 0; | | 1534 | rec->name[sz - 1] = 0; |
1541 | else | | 1535 | else |
1542 | rec->name[sz] = 0; | | 1536 | rec->name[sz] = 0; |
1543 | while (rec->links[sz] == ' ') | | 1537 | while (rec->links[sz] == ' ') |
1544 | ++sz; | | 1538 | ++sz; |
1545 | tmp = estrdup(rec->links + sz); | | 1539 | tmp = estrdup(rec->links + sz); |
1546 | free(rec->links); | | 1540 | free(rec->links); |
1547 | rec->links = tmp; | | 1541 | rec->links = tmp; |
1548 | } | | 1542 | } |
1549 | | | 1543 | |
1550 | /*------------------------ Populate the mandb table---------------------------*/ | | 1544 | /*------------------------ Populate the mandb table---------------------------*/ |
1551 | sqlstr = "INSERT INTO mandb VALUES (:section, :name, :name_desc, :desc," | | 1545 | sqlstr = "INSERT INTO mandb VALUES (:section, :name, :name_desc, :desc," |
1552 | " :lib, :return_vals, :env, :files, :exit_status," | | 1546 | " :lib, :return_vals, :env, :files, :exit_status," |
1553 | " :diagnostics, :errors, :md5_hash, :machine)"; | | 1547 | " :diagnostics, :errors, :md5_hash, :machine)"; |
1554 | | | 1548 | |
1555 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 1549 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
1556 | if (rc != SQLITE_OK) | | 1550 | if (rc != SQLITE_OK) |
1557 | goto Out; | | 1551 | goto Out; |
1558 | | | 1552 | |
1559 | idx = sqlite3_bind_parameter_index(stmt, ":name"); | | 1553 | idx = sqlite3_bind_parameter_index(stmt, ":name"); |
1560 | rc = sqlite3_bind_text(stmt, idx, rec->name, -1, NULL); | | 1554 | rc = sqlite3_bind_text(stmt, idx, rec->name, -1, NULL); |
1561 | if (rc != SQLITE_OK) { | | 1555 | if (rc != SQLITE_OK) { |
1562 | sqlite3_finalize(stmt); | | 1556 | sqlite3_finalize(stmt); |
1563 | goto Out; | | 1557 | goto Out; |
1564 | } | | 1558 | } |
1565 | | | 1559 | |
1566 | idx = sqlite3_bind_parameter_index(stmt, ":section"); | | 1560 | idx = sqlite3_bind_parameter_index(stmt, ":section"); |
1567 | rc = sqlite3_bind_text(stmt, idx, rec->section, -1, NULL); | | 1561 | rc = sqlite3_bind_text(stmt, idx, rec->section, -1, NULL); |
1568 | if (rc != SQLITE_OK) { | | 1562 | if (rc != SQLITE_OK) { |
1569 | sqlite3_finalize(stmt); | | 1563 | sqlite3_finalize(stmt); |
1570 | goto Out; | | 1564 | goto Out; |
1571 | } | | 1565 | } |
1572 | | | 1566 | |
1573 | idx = sqlite3_bind_parameter_index(stmt, ":name_desc"); | | 1567 | idx = sqlite3_bind_parameter_index(stmt, ":name_desc"); |
1574 | rc = sqlite3_bind_text(stmt, idx, rec->name_desc, -1, NULL); | | 1568 | rc = sqlite3_bind_text(stmt, idx, rec->name_desc, -1, NULL); |
1575 | if (rc != SQLITE_OK) { | | 1569 | if (rc != SQLITE_OK) { |
1576 | sqlite3_finalize(stmt); | | 1570 | sqlite3_finalize(stmt); |
1577 | goto Out; | | 1571 | goto Out; |
1578 | } | | 1572 | } |
1579 | | | 1573 | |
1580 | idx = sqlite3_bind_parameter_index(stmt, ":desc"); | | 1574 | idx = sqlite3_bind_parameter_index(stmt, ":desc"); |
1581 | rc = sqlite3_bind_text(stmt, idx, rec->desc.data, | | 1575 | rc = sqlite3_bind_text(stmt, idx, rec->desc.data, |
1582 | rec->desc.offset + 1, NULL); | | 1576 | rec->desc.offset + 1, NULL); |
1583 | if (rc != SQLITE_OK) { | | 1577 | if (rc != SQLITE_OK) { |
1584 | sqlite3_finalize(stmt); | | 1578 | sqlite3_finalize(stmt); |
1585 | goto Out; | | 1579 | goto Out; |
1586 | } | | 1580 | } |
1587 | | | 1581 | |
1588 | idx = sqlite3_bind_parameter_index(stmt, ":lib"); | | 1582 | idx = sqlite3_bind_parameter_index(stmt, ":lib"); |
1589 | rc = sqlite3_bind_text(stmt, idx, rec->lib.data, rec->lib.offset + 1, NULL); | | 1583 | rc = sqlite3_bind_text(stmt, idx, rec->lib.data, rec->lib.offset + 1, NULL); |
1590 | if (rc != SQLITE_OK) { | | 1584 | if (rc != SQLITE_OK) { |
1591 | sqlite3_finalize(stmt); | | 1585 | sqlite3_finalize(stmt); |
1592 | goto Out; | | 1586 | goto Out; |
1593 | } | | 1587 | } |
1594 | | | 1588 | |
1595 | idx = sqlite3_bind_parameter_index(stmt, ":return_vals"); | | 1589 | idx = sqlite3_bind_parameter_index(stmt, ":return_vals"); |
1596 | rc = sqlite3_bind_text(stmt, idx, rec->return_vals.data, | | 1590 | rc = sqlite3_bind_text(stmt, idx, rec->return_vals.data, |
1597 | rec->return_vals.offset + 1, NULL); | | 1591 | rec->return_vals.offset + 1, NULL); |
1598 | if (rc != SQLITE_OK) { | | 1592 | if (rc != SQLITE_OK) { |
1599 | sqlite3_finalize(stmt); | | 1593 | sqlite3_finalize(stmt); |
1600 | goto Out; | | 1594 | goto Out; |
1601 | } | | 1595 | } |
1602 | | | 1596 | |
1603 | idx = sqlite3_bind_parameter_index(stmt, ":env"); | | 1597 | idx = sqlite3_bind_parameter_index(stmt, ":env"); |
1604 | rc = sqlite3_bind_text(stmt, idx, rec->env.data, rec->env.offset + 1, NULL); | | 1598 | rc = sqlite3_bind_text(stmt, idx, rec->env.data, rec->env.offset + 1, NULL); |
1605 | if (rc != SQLITE_OK) { | | 1599 | if (rc != SQLITE_OK) { |
1606 | sqlite3_finalize(stmt); | | 1600 | sqlite3_finalize(stmt); |
1607 | goto Out; | | 1601 | goto Out; |
1608 | } | | 1602 | } |
1609 | | | 1603 | |
1610 | idx = sqlite3_bind_parameter_index(stmt, ":files"); | | 1604 | idx = sqlite3_bind_parameter_index(stmt, ":files"); |
1611 | rc = sqlite3_bind_text(stmt, idx, rec->files.data, | | 1605 | rc = sqlite3_bind_text(stmt, idx, rec->files.data, |
1612 | rec->files.offset + 1, NULL); | | 1606 | rec->files.offset + 1, NULL); |
1613 | if (rc != SQLITE_OK) { | | 1607 | if (rc != SQLITE_OK) { |
1614 | sqlite3_finalize(stmt); | | 1608 | sqlite3_finalize(stmt); |
1615 | goto Out; | | 1609 | goto Out; |
1616 | } | | 1610 | } |
1617 | | | 1611 | |
1618 | idx = sqlite3_bind_parameter_index(stmt, ":exit_status"); | | 1612 | idx = sqlite3_bind_parameter_index(stmt, ":exit_status"); |
1619 | rc = sqlite3_bind_text(stmt, idx, rec->exit_status.data, | | 1613 | rc = sqlite3_bind_text(stmt, idx, rec->exit_status.data, |
1620 | rec->exit_status.offset + 1, NULL); | | 1614 | rec->exit_status.offset + 1, NULL); |
1621 | if (rc != SQLITE_OK) { | | 1615 | if (rc != SQLITE_OK) { |
1622 | sqlite3_finalize(stmt); | | 1616 | sqlite3_finalize(stmt); |
1623 | goto Out; | | 1617 | goto Out; |
1624 | } | | 1618 | } |
1625 | | | 1619 | |
1626 | idx = sqlite3_bind_parameter_index(stmt, ":diagnostics"); | | 1620 | idx = sqlite3_bind_parameter_index(stmt, ":diagnostics"); |
1627 | rc = sqlite3_bind_text(stmt, idx, rec->diagnostics.data, | | 1621 | rc = sqlite3_bind_text(stmt, idx, rec->diagnostics.data, |
1628 | rec->diagnostics.offset + 1, NULL); | | 1622 | rec->diagnostics.offset + 1, NULL); |
1629 | if (rc != SQLITE_OK) { | | 1623 | if (rc != SQLITE_OK) { |
1630 | sqlite3_finalize(stmt); | | 1624 | sqlite3_finalize(stmt); |
1631 | goto Out; | | 1625 | goto Out; |
1632 | } | | 1626 | } |
1633 | | | 1627 | |
1634 | idx = sqlite3_bind_parameter_index(stmt, ":errors"); | | 1628 | idx = sqlite3_bind_parameter_index(stmt, ":errors"); |
1635 | rc = sqlite3_bind_text(stmt, idx, rec->errors.data, | | 1629 | rc = sqlite3_bind_text(stmt, idx, rec->errors.data, |
1636 | rec->errors.offset + 1, NULL); | | 1630 | rec->errors.offset + 1, NULL); |
1637 | if (rc != SQLITE_OK) { | | 1631 | if (rc != SQLITE_OK) { |
1638 | sqlite3_finalize(stmt); | | 1632 | sqlite3_finalize(stmt); |
1639 | goto Out; | | 1633 | goto Out; |
1640 | } | | 1634 | } |
1641 | | | 1635 | |
1642 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); | | 1636 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); |
1643 | rc = sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); | | 1637 | rc = sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); |
1644 | if (rc != SQLITE_OK) { | | 1638 | if (rc != SQLITE_OK) { |
1645 | sqlite3_finalize(stmt); | | 1639 | sqlite3_finalize(stmt); |
1646 | goto Out; | | 1640 | goto Out; |
1647 | } | | 1641 | } |
1648 | | | 1642 | |
1649 | idx = sqlite3_bind_parameter_index(stmt, ":machine"); | | 1643 | idx = sqlite3_bind_parameter_index(stmt, ":machine"); |
1650 | if (rec->machine) | | 1644 | if (rec->machine) |
1651 | rc = sqlite3_bind_text(stmt, idx, rec->machine, -1, NULL); | | 1645 | rc = sqlite3_bind_text(stmt, idx, rec->machine, -1, NULL); |
1652 | else | | 1646 | else |
1653 | rc = sqlite3_bind_null(stmt, idx); | | 1647 | rc = sqlite3_bind_null(stmt, idx); |
1654 | if (rc != SQLITE_OK) { | | 1648 | if (rc != SQLITE_OK) { |
1655 | sqlite3_finalize(stmt); | | 1649 | sqlite3_finalize(stmt); |
1656 | goto Out; | | 1650 | goto Out; |
1657 | } | | 1651 | } |
1658 | | | 1652 | |
1659 | rc = sqlite3_step(stmt); | | 1653 | rc = sqlite3_step(stmt); |
1660 | if (rc != SQLITE_DONE) { | | 1654 | if (rc != SQLITE_DONE) { |
1661 | sqlite3_finalize(stmt); | | 1655 | sqlite3_finalize(stmt); |
1662 | goto Out; | | 1656 | goto Out; |
1663 | } | | 1657 | } |
1664 | | | 1658 | |
1665 | sqlite3_finalize(stmt); | | 1659 | sqlite3_finalize(stmt); |
1666 | | | 1660 | |
1667 | /* Get the row id of the last inserted row */ | | 1661 | /* Get the row id of the last inserted row */ |
1668 | mandb_rowid = sqlite3_last_insert_rowid(db); | | 1662 | mandb_rowid = sqlite3_last_insert_rowid(db); |
1669 | | | 1663 | |
1670 | /*------------------------Populate the mandb_meta table-----------------------*/ | | 1664 | /*------------------------Populate the mandb_meta table-----------------------*/ |
1671 | sqlstr = "INSERT INTO mandb_meta VALUES (:device, :inode, :mtime," | | 1665 | sqlstr = "INSERT INTO mandb_meta VALUES (:device, :inode, :mtime," |
1672 | " :file, :md5_hash, :id)"; | | 1666 | " :file, :md5_hash, :id)"; |
1673 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 1667 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
1674 | if (rc != SQLITE_OK) | | 1668 | if (rc != SQLITE_OK) |
1675 | goto Out; | | 1669 | goto Out; |
1676 | | | 1670 | |
1677 | idx = sqlite3_bind_parameter_index(stmt, ":device"); | | 1671 | idx = sqlite3_bind_parameter_index(stmt, ":device"); |
1678 | rc = sqlite3_bind_int64(stmt, idx, rec->device); | | 1672 | rc = sqlite3_bind_int64(stmt, idx, rec->device); |
1679 | if (rc != SQLITE_OK) { | | 1673 | if (rc != SQLITE_OK) { |
1680 | sqlite3_finalize(stmt); | | 1674 | sqlite3_finalize(stmt); |
1681 | goto Out; | | 1675 | goto Out; |
1682 | } | | 1676 | } |
1683 | | | 1677 | |
1684 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); | | 1678 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); |
1685 | rc = sqlite3_bind_int64(stmt, idx, rec->inode); | | 1679 | rc = sqlite3_bind_int64(stmt, idx, rec->inode); |
1686 | if (rc != SQLITE_OK) { | | 1680 | if (rc != SQLITE_OK) { |
1687 | sqlite3_finalize(stmt); | | 1681 | sqlite3_finalize(stmt); |
1688 | goto Out; | | 1682 | goto Out; |
1689 | } | | 1683 | } |
1690 | | | 1684 | |
1691 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); | | 1685 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); |
1692 | rc = sqlite3_bind_int64(stmt, idx, rec->mtime); | | 1686 | rc = sqlite3_bind_int64(stmt, idx, rec->mtime); |
1693 | if (rc != SQLITE_OK) { | | 1687 | if (rc != SQLITE_OK) { |
1694 | sqlite3_finalize(stmt); | | 1688 | sqlite3_finalize(stmt); |
1695 | goto Out; | | 1689 | goto Out; |
1696 | } | | 1690 | } |
1697 | | | 1691 | |
1698 | idx = sqlite3_bind_parameter_index(stmt, ":file"); | | 1692 | idx = sqlite3_bind_parameter_index(stmt, ":file"); |
1699 | rc = sqlite3_bind_text(stmt, idx, rec->file_path, -1, NULL); | | 1693 | rc = sqlite3_bind_text(stmt, idx, rec->file_path, -1, NULL); |
1700 | if (rc != SQLITE_OK) { | | 1694 | if (rc != SQLITE_OK) { |
1701 | sqlite3_finalize(stmt); | | 1695 | sqlite3_finalize(stmt); |
1702 | goto Out; | | 1696 | goto Out; |
1703 | } | | 1697 | } |
1704 | | | 1698 | |
1705 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); | | 1699 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); |
1706 | rc = sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); | | 1700 | rc = sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); |
1707 | if (rc != SQLITE_OK) { | | 1701 | if (rc != SQLITE_OK) { |
1708 | sqlite3_finalize(stmt); | | 1702 | sqlite3_finalize(stmt); |
1709 | goto Out; | | 1703 | goto Out; |
1710 | } | | 1704 | } |
1711 | | | 1705 | |
1712 | idx = sqlite3_bind_parameter_index(stmt, ":id"); | | 1706 | idx = sqlite3_bind_parameter_index(stmt, ":id"); |
1713 | rc = sqlite3_bind_int64(stmt, idx, mandb_rowid); | | 1707 | rc = sqlite3_bind_int64(stmt, idx, mandb_rowid); |
1714 | if (rc != SQLITE_OK) { | | 1708 | if (rc != SQLITE_OK) { |
1715 | sqlite3_finalize(stmt); | | 1709 | sqlite3_finalize(stmt); |
1716 | goto Out; | | 1710 | goto Out; |
1717 | } | | 1711 | } |
1718 | | | 1712 | |
1719 | rc = sqlite3_step(stmt); | | 1713 | rc = sqlite3_step(stmt); |
1720 | sqlite3_finalize(stmt); | | 1714 | sqlite3_finalize(stmt); |
1721 | if (rc == SQLITE_CONSTRAINT_UNIQUE) { | | 1715 | if (rc == SQLITE_CONSTRAINT_UNIQUE) { |
1722 | /* The *most* probable reason for reaching here is that | | 1716 | /* The *most* probable reason for reaching here is that |
1723 | * the UNIQUE contraint on the file column of the mandb_meta | | 1717 | * the UNIQUE contraint on the file column of the mandb_meta |
1724 | * table was violated. | | 1718 | * table was violated. |
1725 | * This can happen when a file was updated/modified. | | 1719 | * This can happen when a file was updated/modified. |
1726 | * To fix this we need to do two things: | | 1720 | * To fix this we need to do two things: |
1727 | * 1. Delete the row for the older version of this file | | 1721 | * 1. Delete the row for the older version of this file |
1728 | * from mandb table. | | 1722 | * from mandb table. |
1729 | * 2. Run an UPDATE query to update the row for this file | | 1723 | * 2. Run an UPDATE query to update the row for this file |
1730 | * in the mandb_meta table. | | 1724 | * in the mandb_meta table. |
1731 | */ | | 1725 | */ |
1732 | warnx("Trying to update index for %s", rec->file_path); | | 1726 | warnx("Trying to update index for %s", rec->file_path); |
1733 | char *sql = sqlite3_mprintf("DELETE FROM mandb " | | 1727 | char *sql = sqlite3_mprintf("DELETE FROM mandb " |
1734 | "WHERE rowid = (SELECT id" | | 1728 | "WHERE rowid = (SELECT id" |
1735 | " FROM mandb_meta" | | 1729 | " FROM mandb_meta" |
1736 | " WHERE file = %Q)", | | 1730 | " WHERE file = %Q)", |
1737 | rec->file_path); | | 1731 | rec->file_path); |
1738 | sqlite3_exec(db, sql, NULL, NULL, &errmsg); | | 1732 | sqlite3_exec(db, sql, NULL, NULL, &errmsg); |
1739 | sqlite3_free(sql); | | 1733 | sqlite3_free(sql); |
1740 | if (errmsg != NULL) { | | 1734 | if (errmsg != NULL) { |
1741 | if (mflags.verbosity) | | 1735 | if (mflags.verbosity) |
1742 | warnx("%s", errmsg); | | 1736 | warnx("%s", errmsg); |
1743 | free(errmsg); | | 1737 | free(errmsg); |
1744 | } | | 1738 | } |
1745 | sqlstr = "UPDATE mandb_meta SET device = :device," | | 1739 | sqlstr = "UPDATE mandb_meta SET device = :device," |
1746 | " inode = :inode, mtime = :mtime, id = :id," | | 1740 | " inode = :inode, mtime = :mtime, id = :id," |
1747 | " md5_hash = :md5 WHERE file = :file"; | | 1741 | " md5_hash = :md5 WHERE file = :file"; |
1748 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 1742 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
1749 | if (rc != SQLITE_OK) { | | 1743 | if (rc != SQLITE_OK) { |
1750 | if (mflags.verbosity) | | 1744 | if (mflags.verbosity) |
1751 | warnx("Update failed with error: %s", | | 1745 | warnx("Update failed with error: %s", |
1752 | sqlite3_errmsg(db)); | | 1746 | sqlite3_errmsg(db)); |
1753 | close_db(db); | | 1747 | close_db(db); |
1754 | cleanup(rec); | | 1748 | cleanup(rec); |
1755 | errx(EXIT_FAILURE, | | 1749 | errx(EXIT_FAILURE, |
1756 | "Consider running makemandb with -f option"); | | 1750 | "Consider running makemandb with -f option"); |
1757 | } | | 1751 | } |
1758 | | | 1752 | |
1759 | idx = sqlite3_bind_parameter_index(stmt, ":device"); | | 1753 | idx = sqlite3_bind_parameter_index(stmt, ":device"); |
1760 | sqlite3_bind_int64(stmt, idx, rec->device); | | 1754 | sqlite3_bind_int64(stmt, idx, rec->device); |
1761 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); | | 1755 | idx = sqlite3_bind_parameter_index(stmt, ":inode"); |
1762 | sqlite3_bind_int64(stmt, idx, rec->inode); | | 1756 | sqlite3_bind_int64(stmt, idx, rec->inode); |
1763 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); | | 1757 | idx = sqlite3_bind_parameter_index(stmt, ":mtime"); |
1764 | sqlite3_bind_int64(stmt, idx, rec->mtime); | | 1758 | sqlite3_bind_int64(stmt, idx, rec->mtime); |
1765 | idx = sqlite3_bind_parameter_index(stmt, ":id"); | | 1759 | idx = sqlite3_bind_parameter_index(stmt, ":id"); |
1766 | sqlite3_bind_int64(stmt, idx, mandb_rowid); | | 1760 | sqlite3_bind_int64(stmt, idx, mandb_rowid); |
1767 | idx = sqlite3_bind_parameter_index(stmt, ":md5"); | | 1761 | idx = sqlite3_bind_parameter_index(stmt, ":md5"); |
1768 | sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); | | 1762 | sqlite3_bind_text(stmt, idx, rec->md5_hash, -1, NULL); |
1769 | idx = sqlite3_bind_parameter_index(stmt, ":file"); | | 1763 | idx = sqlite3_bind_parameter_index(stmt, ":file"); |
1770 | sqlite3_bind_text(stmt, idx, rec->file_path, -1, NULL); | | 1764 | sqlite3_bind_text(stmt, idx, rec->file_path, -1, NULL); |
1771 | rc = sqlite3_step(stmt); | | 1765 | rc = sqlite3_step(stmt); |
1772 | sqlite3_finalize(stmt); | | 1766 | sqlite3_finalize(stmt); |
1773 | | | 1767 | |
1774 | if (rc != SQLITE_DONE) { | | 1768 | if (rc != SQLITE_DONE) { |
1775 | if (mflags.verbosity) | | 1769 | if (mflags.verbosity) |
1776 | warnx("%s", sqlite3_errmsg(db)); | | 1770 | warnx("%s", sqlite3_errmsg(db)); |
1777 | close_db(db); | | 1771 | close_db(db); |
1778 | cleanup(rec); | | 1772 | cleanup(rec); |
1779 | errx(EXIT_FAILURE, | | 1773 | errx(EXIT_FAILURE, |
1780 | "Consider running makemandb with -f option"); | | 1774 | "Consider running makemandb with -f option"); |
1781 | } | | 1775 | } |
1782 | } else if (rc != SQLITE_DONE) { | | 1776 | } else if (rc != SQLITE_DONE) { |
1783 | /* Otherwise make this error fatal */ | | 1777 | /* Otherwise make this error fatal */ |
1784 | warnx("Failed at %s\n%s", rec->file_path, sqlite3_errmsg(db)); | | 1778 | warnx("Failed at %s\n%s", rec->file_path, sqlite3_errmsg(db)); |
1785 | cleanup(rec); | | 1779 | cleanup(rec); |
1786 | close_db(db); | | 1780 | close_db(db); |
1787 | exit(EXIT_FAILURE); | | 1781 | exit(EXIT_FAILURE); |
1788 | } | | 1782 | } |
1789 | | | 1783 | |
1790 | /*------------------------ Populate the mandb_links table---------------------*/ | | 1784 | /*------------------------ Populate the mandb_links table---------------------*/ |
1791 | char *str = NULL; | | 1785 | char *str = NULL; |
1792 | char *links; | | 1786 | char *links; |
1793 | if (rec->links && strlen(rec->links)) { | | 1787 | if (rec->links && strlen(rec->links)) { |
1794 | links = rec->links; | | 1788 | links = rec->links; |
1795 | for(ln = strtok(links, " "); ln; ln = strtok(NULL, " ")) { | | 1789 | for(ln = strtok(links, " "); ln; ln = strtok(NULL, " ")) { |
1796 | if (ln[0] == ',') | | 1790 | if (ln[0] == ',') |
1797 | ln++; | | 1791 | ln++; |
1798 | if(ln[strlen(ln) - 1] == ',') | | 1792 | if(ln[strlen(ln) - 1] == ',') |
1799 | ln[strlen(ln) - 1] = 0; | | 1793 | ln[strlen(ln) - 1] = 0; |
1800 | | | 1794 | |
1801 | str = sqlite3_mprintf("INSERT INTO mandb_links" | | 1795 | str = sqlite3_mprintf("INSERT INTO mandb_links" |
1802 | " VALUES (%Q, %Q, %Q, %Q, %Q)", | | 1796 | " VALUES (%Q, %Q, %Q, %Q, %Q)", |
1803 | ln, rec->name, rec->section, | | 1797 | ln, rec->name, rec->section, |
1804 | rec->machine, rec->md5_hash); | | 1798 | rec->machine, rec->md5_hash); |
1805 | sqlite3_exec(db, str, NULL, NULL, &errmsg); | | 1799 | sqlite3_exec(db, str, NULL, NULL, &errmsg); |
1806 | sqlite3_free(str); | | 1800 | sqlite3_free(str); |
1807 | if (errmsg != NULL) { | | 1801 | if (errmsg != NULL) { |
1808 | warnx("%s", errmsg); | | 1802 | warnx("%s", errmsg); |
1809 | cleanup(rec); | | 1803 | cleanup(rec); |
1810 | free(errmsg); | | 1804 | free(errmsg); |
1811 | return -1; | | 1805 | return -1; |
1812 | } | | 1806 | } |
1813 | } | | 1807 | } |
1814 | } | | 1808 | } |
1815 | | | 1809 | |
1816 | cleanup(rec); | | 1810 | cleanup(rec); |
1817 | return 0; | | 1811 | return 0; |
1818 | | | 1812 | |
1819 | Out: | | 1813 | Out: |
1820 | if (mflags.verbosity) | | 1814 | if (mflags.verbosity) |
1821 | warnx("%s", sqlite3_errmsg(db)); | | 1815 | warnx("%s", sqlite3_errmsg(db)); |
1822 | cleanup(rec); | | 1816 | cleanup(rec); |
1823 | return -1; | | 1817 | return -1; |
1824 | } | | 1818 | } |
1825 | | | 1819 | |
1826 | /* | | 1820 | /* |
1827 | * check_md5-- | | 1821 | * check_md5-- |
1828 | * Generates the md5 hash of the file and checks if it already doesn't exist | | 1822 | * Generates the md5 hash of the file and checks if it already doesn't exist |
1829 | * in the table (passed as the 3rd parameter). | | 1823 | * in the table (passed as the 3rd parameter). |
1830 | * This function is being used to avoid hardlinks. | | 1824 | * This function is being used to avoid hardlinks. |
1831 | * On successful completion it will also set the value of the fourth parameter | | 1825 | * On successful completion it will also set the value of the fourth parameter |
1832 | * to the md5 hash of the file (computed previously). It is the responsibility | | 1826 | * to the md5 hash of the file (computed previously). It is the responsibility |
1833 | * of the caller to free this buffer. | | 1827 | * of the caller to free this buffer. |
1834 | * Return values: | | 1828 | * Return values: |
1835 | * -1: If an error occurs somewhere and sets the md5 return buffer to NULL. | | 1829 | * -1: If an error occurs somewhere and sets the md5 return buffer to NULL. |
1836 | * 0: If the md5 hash does not exist in the table. | | 1830 | * 0: If the md5 hash does not exist in the table. |
1837 | * 1: If the hash exists in the database. | | 1831 | * 1: If the hash exists in the database. |
1838 | */ | | 1832 | */ |
1839 | static int | | 1833 | static int |
1840 | check_md5(const char *file, sqlite3 *db, const char *table, char **md5sum, | | 1834 | check_md5(const char *file, sqlite3 *db, const char *table, char **md5sum, |
1841 | void *buf, size_t buflen) | | 1835 | void *buf, size_t buflen) |
1842 | { | | 1836 | { |
1843 | int rc = 0; | | 1837 | int rc = 0; |
1844 | int idx = -1; | | 1838 | int idx = -1; |
1845 | char *sqlstr = NULL; | | 1839 | char *sqlstr = NULL; |
1846 | sqlite3_stmt *stmt = NULL; | | 1840 | sqlite3_stmt *stmt = NULL; |
1847 | | | 1841 | |
1848 | assert(file != NULL); | | 1842 | assert(file != NULL); |
1849 | *md5sum = MD5Data(buf, buflen, NULL); | | 1843 | *md5sum = MD5Data(buf, buflen, NULL); |
1850 | if (*md5sum == NULL) { | | 1844 | if (*md5sum == NULL) { |
1851 | if (mflags.verbosity) | | 1845 | if (mflags.verbosity) |
1852 | warn("md5 failed: %s", file); | | 1846 | warn("md5 failed: %s", file); |
1853 | return -1; | | 1847 | return -1; |
1854 | } | | 1848 | } |
1855 | | | 1849 | |
1856 | easprintf(&sqlstr, "SELECT * FROM %s WHERE md5_hash = :md5_hash", | | 1850 | easprintf(&sqlstr, "SELECT * FROM %s WHERE md5_hash = :md5_hash", |
1857 | table); | | 1851 | table); |
1858 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); | | 1852 | rc = sqlite3_prepare_v2(db, sqlstr, -1, &stmt, NULL); |
1859 | if (rc != SQLITE_OK) { | | 1853 | if (rc != SQLITE_OK) { |
1860 | free(sqlstr); | | 1854 | free(sqlstr); |
1861 | free(*md5sum); | | 1855 | free(*md5sum); |
1862 | *md5sum = NULL; | | 1856 | *md5sum = NULL; |
1863 | return -1; | | 1857 | return -1; |
1864 | } | | 1858 | } |
1865 | | | 1859 | |
1866 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); | | 1860 | idx = sqlite3_bind_parameter_index(stmt, ":md5_hash"); |
1867 | rc = sqlite3_bind_text(stmt, idx, *md5sum, -1, NULL); | | 1861 | rc = sqlite3_bind_text(stmt, idx, *md5sum, -1, NULL); |
1868 | if (rc != SQLITE_OK) { | | 1862 | if (rc != SQLITE_OK) { |
1869 | if (mflags.verbosity) | | 1863 | if (mflags.verbosity) |
1870 | warnx("%s", sqlite3_errmsg(db)); | | 1864 | warnx("%s", sqlite3_errmsg(db)); |
1871 | sqlite3_finalize(stmt); | | 1865 | sqlite3_finalize(stmt); |
1872 | free(sqlstr); | | 1866 | free(sqlstr); |
1873 | free(*md5sum); | | 1867 | free(*md5sum); |
1874 | *md5sum = NULL; | | 1868 | *md5sum = NULL; |
1875 | return -1; | | 1869 | return -1; |
1876 | } | | 1870 | } |
1877 | | | 1871 | |
1878 | if (sqlite3_step(stmt) == SQLITE_ROW) { | | 1872 | if (sqlite3_step(stmt) == SQLITE_ROW) { |
1879 | sqlite3_finalize(stmt); | | 1873 | sqlite3_finalize(stmt); |
1880 | free(sqlstr); | | 1874 | free(sqlstr); |
1881 | return 0; | | 1875 | return 0; |
1882 | } | | 1876 | } |
1883 | | | 1877 | |
1884 | sqlite3_finalize(stmt); | | 1878 | sqlite3_finalize(stmt); |
1885 | free(sqlstr); | | 1879 | free(sqlstr); |
1886 | return 1; | | 1880 | return 1; |
1887 | } | | 1881 | } |
1888 | | | 1882 | |
1889 | /* Optimize the index for faster search */ | | 1883 | /* Optimize the index for faster search */ |
1890 | static void | | 1884 | static void |
1891 | optimize(sqlite3 *db) | | 1885 | optimize(sqlite3 *db) |
1892 | { | | 1886 | { |
1893 | const char *sqlstr; | | 1887 | const char *sqlstr; |
1894 | char *errmsg = NULL; | | 1888 | char *errmsg = NULL; |
1895 | | | 1889 | |
1896 | if (mflags.verbosity == 2) | | 1890 | if (mflags.verbosity == 2) |
1897 | printf("Optimizing the database index\n"); | | 1891 | printf("Optimizing the database index\n"); |
1898 | sqlstr = "INSERT INTO mandb(mandb) VALUES (\'optimize\');" | | 1892 | sqlstr = "INSERT INTO mandb(mandb) VALUES (\'optimize\');" |
1899 | "VACUUM"; | | 1893 | "VACUUM"; |
1900 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); | | 1894 | sqlite3_exec(db, sqlstr, NULL, NULL, &errmsg); |
1901 | if (errmsg != NULL) { | | 1895 | if (errmsg != NULL) { |
1902 | if (mflags.verbosity) | | 1896 | if (mflags.verbosity) |
1903 | warnx("%s", errmsg); | | 1897 | warnx("%s", errmsg); |
1904 | free(errmsg); | | 1898 | free(errmsg); |
1905 | return; | | 1899 | return; |
1906 | } | | 1900 | } |
1907 | } | | 1901 | } |
1908 | | | 1902 | |
1909 | /* | | 1903 | /* |
1910 | * cleanup -- | | 1904 | * cleanup -- |
1911 | * cleans up the global buffers | | 1905 | * cleans up the global buffers |
1912 | */ | | 1906 | */ |
1913 | static void | | 1907 | static void |
1914 | cleanup(mandb_rec *rec) | | 1908 | cleanup(mandb_rec *rec) |
1915 | { | | 1909 | { |
1916 | rec->desc.offset = 0; | | 1910 | rec->desc.offset = 0; |
1917 | rec->lib.offset = 0; | | 1911 | rec->lib.offset = 0; |
1918 | rec->return_vals.offset = 0; | | 1912 | rec->return_vals.offset = 0; |
1919 | rec->env.offset = 0; | | 1913 | rec->env.offset = 0; |
1920 | rec->exit_status.offset = 0; | | 1914 | rec->exit_status.offset = 0; |
1921 | rec->diagnostics.offset = 0; | | 1915 | rec->diagnostics.offset = 0; |
1922 | rec->errors.offset = 0; | | 1916 | rec->errors.offset = 0; |
1923 | rec->files.offset = 0; | | 1917 | rec->files.offset = 0; |
1924 | | | 1918 | |
1925 | free(rec->machine); | | 1919 | free(rec->machine); |
1926 | rec->machine = NULL; | | 1920 | rec->machine = NULL; |
1927 | | | 1921 | |
1928 | free(rec->links); | | 1922 | free(rec->links); |
1929 | rec->links = NULL; | | 1923 | rec->links = NULL; |
1930 | | | 1924 | |
1931 | free(rec->file_path); | | 1925 | free(rec->file_path); |
1932 | rec->file_path = NULL; | | 1926 | rec->file_path = NULL; |
1933 | | | 1927 | |
1934 | free(rec->name); | | 1928 | free(rec->name); |
1935 | rec->name = NULL; | | 1929 | rec->name = NULL; |
1936 | | | 1930 | |
1937 | free(rec->name_desc); | | 1931 | free(rec->name_desc); |
1938 | rec->name_desc = NULL; | | 1932 | rec->name_desc = NULL; |
1939 | | | 1933 | |
1940 | free(rec->md5_hash); | | 1934 | free(rec->md5_hash); |
1941 | rec->md5_hash = NULL; | | 1935 | rec->md5_hash = NULL; |
1942 | } | | 1936 | } |
1943 | | | 1937 | |
1944 | /* | | 1938 | /* |
1945 | * init_secbuffs-- | | 1939 | * init_secbuffs-- |
1946 | * Sets the value of buflen for all the sec_buff field of rec. And then | | 1940 | * Sets the value of buflen for all the sec_buff field of rec. And then |
1947 | * allocate memory to each sec_buff member of rec. | | 1941 | * allocate memory to each sec_buff member of rec. |
1948 | */ | | 1942 | */ |
1949 | static void | | 1943 | static void |
1950 | init_secbuffs(mandb_rec *rec) | | 1944 | init_secbuffs(mandb_rec *rec) |
1951 | { | | 1945 | { |
1952 | /* | | 1946 | /* |
1953 | * Some sec_buff might need more memory, for example desc, | | 1947 | * Some sec_buff might need more memory, for example desc, |
1954 | * which stores the data of the DESCRIPTION section, | | 1948 | * which stores the data of the DESCRIPTION section, |
1955 | * while some might need very small amount of memory. | | 1949 | * while some might need very small amount of memory. |
1956 | * Therefore explicitly setting the value of buflen field for | | 1950 | * Therefore explicitly setting the value of buflen field for |
1957 | * each sec_buff. | | 1951 | * each sec_buff. |
1958 | */ | | 1952 | */ |
1959 | rec->desc.buflen = 10 * BUFLEN; | | 1953 | rec->desc.buflen = 10 * BUFLEN; |
1960 | rec->desc.data = emalloc(rec->desc.buflen); | | 1954 | rec->desc.data = emalloc(rec->desc.buflen); |
1961 | rec->desc.offset = 0; | | 1955 | rec->desc.offset = 0; |
1962 | | | 1956 | |
1963 | rec->lib.buflen = BUFLEN / 2; | | 1957 | rec->lib.buflen = BUFLEN / 2; |
1964 | rec->lib.data = emalloc(rec->lib.buflen); | | 1958 | rec->lib.data = emalloc(rec->lib.buflen); |
1965 | rec->lib.offset = 0; | | 1959 | rec->lib.offset = 0; |
1966 | | | 1960 | |
1967 | rec->return_vals.buflen = BUFLEN; | | 1961 | rec->return_vals.buflen = BUFLEN; |
1968 | rec->return_vals.data = emalloc(rec->return_vals.buflen); | | 1962 | rec->return_vals.data = emalloc(rec->return_vals.buflen); |
1969 | rec->return_vals.offset = 0; | | 1963 | rec->return_vals.offset = 0; |
1970 | | | 1964 | |
1971 | rec->exit_status.buflen = BUFLEN; | | 1965 | rec->exit_status.buflen = BUFLEN; |
1972 | rec->exit_status.data = emalloc(rec->exit_status.buflen); | | 1966 | rec->exit_status.data = emalloc(rec->exit_status.buflen); |
1973 | rec->exit_status.offset = 0; | | 1967 | rec->exit_status.offset = 0; |
1974 | | | 1968 | |
1975 | rec->env.buflen = BUFLEN; | | 1969 | rec->env.buflen = BUFLEN; |
1976 | rec->env.data = emalloc(rec->env.buflen); | | 1970 | rec->env.data = emalloc(rec->env.buflen); |
1977 | rec->env.offset = 0; | | 1971 | rec->env.offset = 0; |
1978 | | | 1972 | |
1979 | rec->files.buflen = BUFLEN; | | 1973 | rec->files.buflen = BUFLEN; |
1980 | rec->files.data = emalloc(rec->files.buflen); | | 1974 | rec->files.data = emalloc(rec->files.buflen); |
1981 | rec->files.offset = 0; | | 1975 | rec->files.offset = 0; |
1982 | | | 1976 | |
1983 | rec->diagnostics.buflen = BUFLEN; | | 1977 | rec->diagnostics.buflen = BUFLEN; |
1984 | rec->diagnostics.data = emalloc(rec->diagnostics.buflen); | | 1978 | rec->diagnostics.data = emalloc(rec->diagnostics.buflen); |
1985 | rec->diagnostics.offset = 0; | | 1979 | rec->diagnostics.offset = 0; |
1986 | | | 1980 | |
1987 | rec->errors.buflen = BUFLEN; | | 1981 | rec->errors.buflen = BUFLEN; |
1988 | rec->errors.data = emalloc(rec->errors.buflen); | | 1982 | rec->errors.data = emalloc(rec->errors.buflen); |
1989 | rec->errors.offset = 0; | | 1983 | rec->errors.offset = 0; |
1990 | } | | 1984 | } |
1991 | | | 1985 | |
1992 | /* | | 1986 | /* |
1993 | * free_secbuffs-- | | 1987 | * free_secbuffs-- |
1994 | * This function should be called at the end, when all the pages have been | | 1988 | * This function should be called at the end, when all the pages have been |
1995 | * parsed. | | 1989 | * parsed. |
1996 | * It frees the memory allocated to sec_buffs by init_secbuffs in the starting. | | 1990 | * It frees the memory allocated to sec_buffs by init_secbuffs in the starting. |
1997 | */ | | 1991 | */ |
1998 | static void | | 1992 | static void |
1999 | free_secbuffs(mandb_rec *rec) | | 1993 | free_secbuffs(mandb_rec *rec) |
2000 | { | | 1994 | { |
2001 | free(rec->desc.data); | | 1995 | free(rec->desc.data); |
2002 | free(rec->lib.data); | | 1996 | free(rec->lib.data); |
2003 | free(rec->return_vals.data); | | 1997 | free(rec->return_vals.data); |
2004 | free(rec->exit_status.data); | | 1998 | free(rec->exit_status.data); |
2005 | free(rec->env.data); | | 1999 | free(rec->env.data); |
2006 | free(rec->files.data); | | 2000 | free(rec->files.data); |
2007 | free(rec->diagnostics.data); | | 2001 | free(rec->diagnostics.data); |
2008 | free(rec->errors.data); | | 2002 | free(rec->errors.data); |
2009 | } | | 2003 | } |
2010 | | | 2004 | |
2011 | static void | | 2005 | static void |
2012 | replace_hyph(char *str) | | 2006 | replace_hyph(char *str) |
2013 | { | | 2007 | { |
2014 | char *iter = str; | | 2008 | char *iter = str; |
2015 | while ((iter = strchr(iter, ASCII_HYPH)) != NULL) | | 2009 | while ((iter = strchr(iter, ASCII_HYPH)) != NULL) |
2016 | *iter = '-'; | | 2010 | *iter = '-'; |
2017 | | | 2011 | |
2018 | iter = str; | | 2012 | iter = str; |
2019 | while ((iter = strchr(iter, ASCII_NBRSP)) != NULL) | | 2013 | while ((iter = strchr(iter, ASCII_NBRSP)) != NULL) |
2020 | *iter = '-'; | | 2014 | *iter = '-'; |
2021 | } | | 2015 | } |
2022 | | | 2016 | |
2023 | static char * | | 2017 | static char * |
2024 | parse_escape(const char *str) | | 2018 | parse_escape(const char *str) |
2025 | { | | 2019 | { |
2026 | const char *backslash, *last_backslash; | | 2020 | const char *backslash, *last_backslash; |
2027 | char *result, *iter; | | 2021 | char *result, *iter; |
2028 | size_t len; | | 2022 | size_t len; |
2029 | | | 2023 | |
2030 | assert(str); | | 2024 | assert(str); |
2031 | | | 2025 | |
2032 | last_backslash = str; | | 2026 | last_backslash = str; |
2033 | backslash = strchr(str, '\\'); | | 2027 | backslash = strchr(str, '\\'); |
2034 | if (backslash == NULL) { | | 2028 | if (backslash == NULL) { |
2035 | result = estrdup(str); | | 2029 | result = estrdup(str); |
2036 | replace_hyph(result); | | 2030 | replace_hyph(result); |
2037 | return result; | | 2031 | return result; |
2038 | } | | 2032 | } |
2039 | | | 2033 | |
2040 | result = emalloc(strlen(str) + 1); | | 2034 | result = emalloc(strlen(str) + 1); |
2041 | iter = result; | | 2035 | iter = result; |
2042 | | | 2036 | |
2043 | do { | | 2037 | do { |
2044 | len = backslash - last_backslash; | | 2038 | len = backslash - last_backslash; |
2045 | memcpy(iter, last_backslash, len); | | 2039 | memcpy(iter, last_backslash, len); |
2046 | iter += len; | | 2040 | iter += len; |
2047 | if (backslash[1] == '-' || backslash[1] == ' ') { | | 2041 | if (backslash[1] == '-' || backslash[1] == ' ') { |
2048 | *iter++ = backslash[1]; | | 2042 | *iter++ = backslash[1]; |
2049 | last_backslash = backslash + 2; | | 2043 | last_backslash = backslash + 2; |
2050 | backslash = strchr(backslash + 2, '\\'); | | 2044 | backslash = strchr(backslash + 2, '\\'); |
2051 | } else { | | 2045 | } else { |
2052 | ++backslash; | | 2046 | ++backslash; |
2053 | mandoc_escape(&backslash, NULL, NULL); | | 2047 | mandoc_escape(&backslash, NULL, NULL); |
2054 | last_backslash = backslash; | | 2048 | last_backslash = backslash; |
2055 | if (backslash == NULL) | | 2049 | if (backslash == NULL) |
2056 | break; | | 2050 | break; |
2057 | backslash = strchr(last_backslash, '\\'); | | 2051 | backslash = strchr(last_backslash, '\\'); |
2058 | } | | 2052 | } |
2059 | } while (backslash != NULL); | | 2053 | } while (backslash != NULL); |
2060 | if (last_backslash != NULL) | | 2054 | if (last_backslash != NULL) |