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