1*4c07abdbSAlexander Ziaee /* $Id: mandocdb.c,v 1.275 2025/06/05 12:33:41 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*4c07abdbSAlexander Ziaee * Copyright (c) 2011-2021, 2024, 2025 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin * Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
661d06d6bSBaptiste Daroussin *
761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin *
1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
186d38604fSBaptiste Daroussin *
196d38604fSBaptiste Daroussin * Implementation of the makewhatis(8) program.
2061d06d6bSBaptiste Daroussin */
2161d06d6bSBaptiste Daroussin #include "config.h"
2261d06d6bSBaptiste Daroussin
2361d06d6bSBaptiste Daroussin #include <sys/types.h>
2461d06d6bSBaptiste Daroussin #include <sys/mman.h>
2561d06d6bSBaptiste Daroussin #include <sys/stat.h>
2661d06d6bSBaptiste Daroussin
2761d06d6bSBaptiste Daroussin #include <assert.h>
2861d06d6bSBaptiste Daroussin #include <ctype.h>
2961d06d6bSBaptiste Daroussin #if HAVE_ERR
3061d06d6bSBaptiste Daroussin #include <err.h>
3161d06d6bSBaptiste Daroussin #endif
3261d06d6bSBaptiste Daroussin #include <errno.h>
3361d06d6bSBaptiste Daroussin #include <fcntl.h>
3461d06d6bSBaptiste Daroussin #if HAVE_FTS
3561d06d6bSBaptiste Daroussin #include <fts.h>
3661d06d6bSBaptiste Daroussin #else
3761d06d6bSBaptiste Daroussin #include "compat_fts.h"
3861d06d6bSBaptiste Daroussin #endif
3961d06d6bSBaptiste Daroussin #include <limits.h>
4061d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT
4161d06d6bSBaptiste Daroussin #include <sandbox.h>
4261d06d6bSBaptiste Daroussin #endif
4361d06d6bSBaptiste Daroussin #include <stdarg.h>
4461d06d6bSBaptiste Daroussin #include <stddef.h>
4561d06d6bSBaptiste Daroussin #include <stdio.h>
4661d06d6bSBaptiste Daroussin #include <stdint.h>
4761d06d6bSBaptiste Daroussin #include <stdlib.h>
4861d06d6bSBaptiste Daroussin #include <string.h>
4961d06d6bSBaptiste Daroussin #include <unistd.h>
5061d06d6bSBaptiste Daroussin
5161d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
5261d06d6bSBaptiste Daroussin #include "mandoc_ohash.h"
5361d06d6bSBaptiste Daroussin #include "mandoc.h"
5461d06d6bSBaptiste Daroussin #include "roff.h"
5561d06d6bSBaptiste Daroussin #include "mdoc.h"
5661d06d6bSBaptiste Daroussin #include "man.h"
577295610fSBaptiste Daroussin #include "mandoc_parse.h"
5861d06d6bSBaptiste Daroussin #include "manconf.h"
5961d06d6bSBaptiste Daroussin #include "mansearch.h"
6061d06d6bSBaptiste Daroussin #include "dba_array.h"
6161d06d6bSBaptiste Daroussin #include "dba.h"
6261d06d6bSBaptiste Daroussin
6361d06d6bSBaptiste Daroussin extern const char *const mansearch_keynames[];
6461d06d6bSBaptiste Daroussin
6561d06d6bSBaptiste Daroussin enum op {
6661d06d6bSBaptiste Daroussin OP_DEFAULT = 0, /* new dbs from dir list or default config */
6761d06d6bSBaptiste Daroussin OP_CONFFILE, /* new databases from custom config file */
6861d06d6bSBaptiste Daroussin OP_UPDATE, /* delete/add entries in existing database */
6961d06d6bSBaptiste Daroussin OP_DELETE, /* delete entries from existing database */
7061d06d6bSBaptiste Daroussin OP_TEST /* change no databases, report potential problems */
7161d06d6bSBaptiste Daroussin };
7261d06d6bSBaptiste Daroussin
7361d06d6bSBaptiste Daroussin struct str {
7461d06d6bSBaptiste Daroussin const struct mpage *mpage; /* if set, the owning parse */
7561d06d6bSBaptiste Daroussin uint64_t mask; /* bitmask in sequence */
7661d06d6bSBaptiste Daroussin char key[]; /* rendered text */
7761d06d6bSBaptiste Daroussin };
7861d06d6bSBaptiste Daroussin
7961d06d6bSBaptiste Daroussin struct inodev {
8061d06d6bSBaptiste Daroussin ino_t st_ino;
8161d06d6bSBaptiste Daroussin dev_t st_dev;
8261d06d6bSBaptiste Daroussin };
8361d06d6bSBaptiste Daroussin
8461d06d6bSBaptiste Daroussin struct mpage {
8561d06d6bSBaptiste Daroussin struct inodev inodev; /* used for hashing routine */
8661d06d6bSBaptiste Daroussin struct dba_array *dba;
8761d06d6bSBaptiste Daroussin char *sec; /* section from file content */
8861d06d6bSBaptiste Daroussin char *arch; /* architecture from file content */
8961d06d6bSBaptiste Daroussin char *title; /* title from file content */
9061d06d6bSBaptiste Daroussin char *desc; /* description from file content */
9161d06d6bSBaptiste Daroussin struct mpage *next; /* singly linked list */
9261d06d6bSBaptiste Daroussin struct mlink *mlinks; /* singly linked list */
9361d06d6bSBaptiste Daroussin int name_head_done;
9461d06d6bSBaptiste Daroussin enum form form; /* format from file content */
9561d06d6bSBaptiste Daroussin };
9661d06d6bSBaptiste Daroussin
9761d06d6bSBaptiste Daroussin struct mlink {
9861d06d6bSBaptiste Daroussin char file[PATH_MAX]; /* filename rel. to manpath */
9961d06d6bSBaptiste Daroussin char *dsec; /* section from directory */
10061d06d6bSBaptiste Daroussin char *arch; /* architecture from directory */
10161d06d6bSBaptiste Daroussin char *name; /* name from file name (not empty) */
10261d06d6bSBaptiste Daroussin char *fsec; /* section from file name suffix */
10361d06d6bSBaptiste Daroussin struct mlink *next; /* singly linked list */
10461d06d6bSBaptiste Daroussin struct mpage *mpage; /* parent */
10561d06d6bSBaptiste Daroussin int gzip; /* filename has a .gz suffix */
10661d06d6bSBaptiste Daroussin enum form dform; /* format from directory */
10761d06d6bSBaptiste Daroussin enum form fform; /* format from file name suffix */
10861d06d6bSBaptiste Daroussin };
10961d06d6bSBaptiste Daroussin
11061d06d6bSBaptiste Daroussin typedef int (*mdoc_fp)(struct mpage *, const struct roff_meta *,
11161d06d6bSBaptiste Daroussin const struct roff_node *);
11261d06d6bSBaptiste Daroussin
11361d06d6bSBaptiste Daroussin struct mdoc_handler {
11461d06d6bSBaptiste Daroussin mdoc_fp fp; /* optional handler */
11561d06d6bSBaptiste Daroussin uint64_t mask; /* set unless handler returns 0 */
11661d06d6bSBaptiste Daroussin int taboo; /* node flags that must not be set */
11761d06d6bSBaptiste Daroussin };
11861d06d6bSBaptiste Daroussin
11961d06d6bSBaptiste Daroussin
12061d06d6bSBaptiste Daroussin int mandocdb(int, char *[]);
12161d06d6bSBaptiste Daroussin
12261d06d6bSBaptiste Daroussin static void dbadd(struct dba *, struct mpage *);
1236d38604fSBaptiste Daroussin static void dbadd_mlink(const struct mlink *);
12461d06d6bSBaptiste Daroussin static void dbprune(struct dba *);
12561d06d6bSBaptiste Daroussin static void dbwrite(struct dba *);
12661d06d6bSBaptiste Daroussin static void filescan(const char *);
12761d06d6bSBaptiste Daroussin #if HAVE_FTS_COMPARE_CONST
12861d06d6bSBaptiste Daroussin static int fts_compare(const FTSENT *const *, const FTSENT *const *);
12961d06d6bSBaptiste Daroussin #else
13061d06d6bSBaptiste Daroussin static int fts_compare(const FTSENT **, const FTSENT **);
13161d06d6bSBaptiste Daroussin #endif
13261d06d6bSBaptiste Daroussin static void mlink_add(struct mlink *, const struct stat *);
13361d06d6bSBaptiste Daroussin static void mlink_check(struct mpage *, struct mlink *);
13461d06d6bSBaptiste Daroussin static void mlink_free(struct mlink *);
13561d06d6bSBaptiste Daroussin static void mlinks_undupe(struct mpage *);
13661d06d6bSBaptiste Daroussin static void mpages_free(void);
13761d06d6bSBaptiste Daroussin static void mpages_merge(struct dba *, struct mparse *);
13861d06d6bSBaptiste Daroussin static void parse_cat(struct mpage *, int);
13961d06d6bSBaptiste Daroussin static void parse_man(struct mpage *, const struct roff_meta *,
14061d06d6bSBaptiste Daroussin const struct roff_node *);
14161d06d6bSBaptiste Daroussin static void parse_mdoc(struct mpage *, const struct roff_meta *,
14261d06d6bSBaptiste Daroussin const struct roff_node *);
14361d06d6bSBaptiste Daroussin static int parse_mdoc_head(struct mpage *, const struct roff_meta *,
14461d06d6bSBaptiste Daroussin const struct roff_node *);
14561d06d6bSBaptiste Daroussin static int parse_mdoc_Fa(struct mpage *, const struct roff_meta *,
14661d06d6bSBaptiste Daroussin const struct roff_node *);
14761d06d6bSBaptiste Daroussin static int parse_mdoc_Fd(struct mpage *, const struct roff_meta *,
14861d06d6bSBaptiste Daroussin const struct roff_node *);
14961d06d6bSBaptiste Daroussin static void parse_mdoc_fname(struct mpage *, const struct roff_node *);
15061d06d6bSBaptiste Daroussin static int parse_mdoc_Fn(struct mpage *, const struct roff_meta *,
15161d06d6bSBaptiste Daroussin const struct roff_node *);
15261d06d6bSBaptiste Daroussin static int parse_mdoc_Fo(struct mpage *, const struct roff_meta *,
15361d06d6bSBaptiste Daroussin const struct roff_node *);
154*4c07abdbSAlexander Ziaee static int parse_mdoc_Lb(struct mpage *, const struct roff_meta *,
155*4c07abdbSAlexander Ziaee const struct roff_node *);
15661d06d6bSBaptiste Daroussin static int parse_mdoc_Nd(struct mpage *, const struct roff_meta *,
15761d06d6bSBaptiste Daroussin const struct roff_node *);
15861d06d6bSBaptiste Daroussin static int parse_mdoc_Nm(struct mpage *, const struct roff_meta *,
15961d06d6bSBaptiste Daroussin const struct roff_node *);
16061d06d6bSBaptiste Daroussin static int parse_mdoc_Sh(struct mpage *, const struct roff_meta *,
16161d06d6bSBaptiste Daroussin const struct roff_node *);
16261d06d6bSBaptiste Daroussin static int parse_mdoc_Va(struct mpage *, const struct roff_meta *,
16361d06d6bSBaptiste Daroussin const struct roff_node *);
16461d06d6bSBaptiste Daroussin static int parse_mdoc_Xr(struct mpage *, const struct roff_meta *,
16561d06d6bSBaptiste Daroussin const struct roff_node *);
16661d06d6bSBaptiste Daroussin static void putkey(const struct mpage *, char *, uint64_t);
16761d06d6bSBaptiste Daroussin static void putkeys(const struct mpage *, char *, size_t, uint64_t);
16861d06d6bSBaptiste Daroussin static void putmdockey(const struct mpage *,
16961d06d6bSBaptiste Daroussin const struct roff_node *, uint64_t, int);
1706d38604fSBaptiste Daroussin #ifdef READ_ALLOWED_PATH
1716d38604fSBaptiste Daroussin static int read_allowed(const char *);
1726d38604fSBaptiste Daroussin #endif
17361d06d6bSBaptiste Daroussin static int render_string(char **, size_t *);
17461d06d6bSBaptiste Daroussin static void say(const char *, const char *, ...)
17561d06d6bSBaptiste Daroussin __attribute__((__format__ (__printf__, 2, 3)));
17661d06d6bSBaptiste Daroussin static int set_basedir(const char *, int);
17761d06d6bSBaptiste Daroussin static int treescan(void);
178c1c95addSBrooks Davis static size_t utf8(unsigned int, char[5]);
17961d06d6bSBaptiste Daroussin
18061d06d6bSBaptiste Daroussin static int nodb; /* no database changes */
18161d06d6bSBaptiste Daroussin static int mparse_options; /* abort the parse early */
18261d06d6bSBaptiste Daroussin static int use_all; /* use all found files */
18361d06d6bSBaptiste Daroussin static int debug; /* print what we're doing */
18461d06d6bSBaptiste Daroussin static int warnings; /* warn about crap */
18561d06d6bSBaptiste Daroussin static int write_utf8; /* write UTF-8 output; else ASCII */
18661d06d6bSBaptiste Daroussin static int exitcode; /* to be returned by main */
18761d06d6bSBaptiste Daroussin static enum op op; /* operational mode */
18861d06d6bSBaptiste Daroussin static char basedir[PATH_MAX]; /* current base directory */
1896d38604fSBaptiste Daroussin static size_t basedir_len; /* strlen(basedir) */
19061d06d6bSBaptiste Daroussin static struct mpage *mpage_head; /* list of distinct manual pages */
19161d06d6bSBaptiste Daroussin static struct ohash mpages; /* table of distinct manual pages */
19261d06d6bSBaptiste Daroussin static struct ohash mlinks; /* table of directory entries */
19361d06d6bSBaptiste Daroussin static struct ohash names; /* table of all names */
19461d06d6bSBaptiste Daroussin static struct ohash strings; /* table of all strings */
19561d06d6bSBaptiste Daroussin static uint64_t name_mask;
19661d06d6bSBaptiste Daroussin
1977295610fSBaptiste Daroussin static const struct mdoc_handler mdoc_handlers[MDOC_MAX - MDOC_Dd] = {
19861d06d6bSBaptiste Daroussin { NULL, 0, NODE_NOPRT }, /* Dd */
19961d06d6bSBaptiste Daroussin { NULL, 0, NODE_NOPRT }, /* Dt */
20061d06d6bSBaptiste Daroussin { NULL, 0, NODE_NOPRT }, /* Os */
20161d06d6bSBaptiste Daroussin { parse_mdoc_Sh, TYPE_Sh, 0 }, /* Sh */
20261d06d6bSBaptiste Daroussin { parse_mdoc_head, TYPE_Ss, 0 }, /* Ss */
20361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Pp */
20461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* D1 */
20561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Dl */
20661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bd */
20761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ed */
20861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bl */
20961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* El */
21061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* It */
21161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ad */
21261d06d6bSBaptiste Daroussin { NULL, TYPE_An, 0 }, /* An */
21361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ap */
21461d06d6bSBaptiste Daroussin { NULL, TYPE_Ar, 0 }, /* Ar */
21561d06d6bSBaptiste Daroussin { NULL, TYPE_Cd, 0 }, /* Cd */
21661d06d6bSBaptiste Daroussin { NULL, TYPE_Cm, 0 }, /* Cm */
21761d06d6bSBaptiste Daroussin { NULL, TYPE_Dv, 0 }, /* Dv */
21861d06d6bSBaptiste Daroussin { NULL, TYPE_Er, 0 }, /* Er */
21961d06d6bSBaptiste Daroussin { NULL, TYPE_Ev, 0 }, /* Ev */
22061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ex */
22161d06d6bSBaptiste Daroussin { parse_mdoc_Fa, 0, 0 }, /* Fa */
22261d06d6bSBaptiste Daroussin { parse_mdoc_Fd, 0, 0 }, /* Fd */
22361d06d6bSBaptiste Daroussin { NULL, TYPE_Fl, 0 }, /* Fl */
22461d06d6bSBaptiste Daroussin { parse_mdoc_Fn, 0, 0 }, /* Fn */
22561d06d6bSBaptiste Daroussin { NULL, TYPE_Ft | TYPE_Vt, 0 }, /* Ft */
22661d06d6bSBaptiste Daroussin { NULL, TYPE_Ic, 0 }, /* Ic */
22761d06d6bSBaptiste Daroussin { NULL, TYPE_In, 0 }, /* In */
22861d06d6bSBaptiste Daroussin { NULL, TYPE_Li, 0 }, /* Li */
22961d06d6bSBaptiste Daroussin { parse_mdoc_Nd, 0, 0 }, /* Nd */
23061d06d6bSBaptiste Daroussin { parse_mdoc_Nm, 0, 0 }, /* Nm */
23161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Op */
23261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ot */
23361d06d6bSBaptiste Daroussin { NULL, TYPE_Pa, NODE_NOSRC }, /* Pa */
23461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Rv */
23561d06d6bSBaptiste Daroussin { NULL, TYPE_St, 0 }, /* St */
23661d06d6bSBaptiste Daroussin { parse_mdoc_Va, TYPE_Va, 0 }, /* Va */
23761d06d6bSBaptiste Daroussin { parse_mdoc_Va, TYPE_Vt, 0 }, /* Vt */
23861d06d6bSBaptiste Daroussin { parse_mdoc_Xr, 0, 0 }, /* Xr */
23961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %A */
24061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %B */
24161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %D */
24261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %I */
24361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %J */
24461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %N */
24561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %O */
24661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %P */
24761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %R */
24861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %T */
24961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %V */
25061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ac */
25161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ao */
25261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Aq */
25361d06d6bSBaptiste Daroussin { NULL, TYPE_At, 0 }, /* At */
25461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bc */
25561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bf */
25661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bo */
25761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bq */
25861d06d6bSBaptiste Daroussin { NULL, TYPE_Bsx, NODE_NOSRC }, /* Bsx */
25961d06d6bSBaptiste Daroussin { NULL, TYPE_Bx, NODE_NOSRC }, /* Bx */
26061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Db */
26161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Dc */
26261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Do */
26361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Dq */
26461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ec */
26561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ef */
26661d06d6bSBaptiste Daroussin { NULL, TYPE_Em, 0 }, /* Em */
26761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Eo */
26861d06d6bSBaptiste Daroussin { NULL, TYPE_Fx, NODE_NOSRC }, /* Fx */
26961d06d6bSBaptiste Daroussin { NULL, TYPE_Ms, 0 }, /* Ms */
27061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* No */
27161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ns */
27261d06d6bSBaptiste Daroussin { NULL, TYPE_Nx, NODE_NOSRC }, /* Nx */
27361d06d6bSBaptiste Daroussin { NULL, TYPE_Ox, NODE_NOSRC }, /* Ox */
27461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Pc */
27561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Pf */
27661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Po */
27761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Pq */
27861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Qc */
27961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ql */
28061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Qo */
28161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Qq */
28261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Re */
28361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Rs */
28461d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Sc */
28561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* So */
28661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Sq */
28761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Sm */
28861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Sx */
28961d06d6bSBaptiste Daroussin { NULL, TYPE_Sy, 0 }, /* Sy */
29061d06d6bSBaptiste Daroussin { NULL, TYPE_Tn, 0 }, /* Tn */
29161d06d6bSBaptiste Daroussin { NULL, 0, NODE_NOSRC }, /* Ux */
29261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Xc */
29361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Xo */
29461d06d6bSBaptiste Daroussin { parse_mdoc_Fo, 0, 0 }, /* Fo */
29561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Fc */
29661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Oo */
29761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Oc */
29861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bk */
29961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ek */
30061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bt */
30161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Hf */
30261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Fr */
30361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ud */
304*4c07abdbSAlexander Ziaee { parse_mdoc_Lb, 0, 0 }, /* Lb */
30561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Lp */
30661d06d6bSBaptiste Daroussin { NULL, TYPE_Lk, 0 }, /* Lk */
30761d06d6bSBaptiste Daroussin { NULL, TYPE_Mt, NODE_NOSRC }, /* Mt */
30861d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Brq */
30961d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Bro */
31061d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Brc */
31161d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %C */
31261d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Es */
31361d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* En */
31461d06d6bSBaptiste Daroussin { NULL, TYPE_Dx, NODE_NOSRC }, /* Dx */
31561d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %Q */
31661d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* %U */
31761d06d6bSBaptiste Daroussin { NULL, 0, 0 }, /* Ta */
31861d06d6bSBaptiste Daroussin };
31961d06d6bSBaptiste Daroussin
32061d06d6bSBaptiste Daroussin
32161d06d6bSBaptiste Daroussin int
mandocdb(int argc,char * argv[])32261d06d6bSBaptiste Daroussin mandocdb(int argc, char *argv[])
32361d06d6bSBaptiste Daroussin {
32461d06d6bSBaptiste Daroussin struct manconf conf;
32561d06d6bSBaptiste Daroussin struct mparse *mp;
32661d06d6bSBaptiste Daroussin struct dba *dba;
32761d06d6bSBaptiste Daroussin const char *path_arg, *progname;
32861d06d6bSBaptiste Daroussin size_t j, sz;
32961d06d6bSBaptiste Daroussin int ch, i;
33061d06d6bSBaptiste Daroussin
33161d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
33261d06d6bSBaptiste Daroussin if (pledge("stdio rpath wpath cpath", NULL) == -1) {
33361d06d6bSBaptiste Daroussin warn("pledge");
33461d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_SYSERR;
33561d06d6bSBaptiste Daroussin }
33661d06d6bSBaptiste Daroussin #endif
33761d06d6bSBaptiste Daroussin
33861d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT
33961d06d6bSBaptiste Daroussin if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) {
34061d06d6bSBaptiste Daroussin warnx("sandbox_init");
34161d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_SYSERR;
34261d06d6bSBaptiste Daroussin }
34361d06d6bSBaptiste Daroussin #endif
34461d06d6bSBaptiste Daroussin
34561d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf));
34661d06d6bSBaptiste Daroussin
34761d06d6bSBaptiste Daroussin /*
34861d06d6bSBaptiste Daroussin * We accept a few different invocations.
34961d06d6bSBaptiste Daroussin * The CHECKOP macro makes sure that invocation styles don't
35061d06d6bSBaptiste Daroussin * clobber each other.
35161d06d6bSBaptiste Daroussin */
35261d06d6bSBaptiste Daroussin #define CHECKOP(_op, _ch) do \
3536d38604fSBaptiste Daroussin if ((_op) != OP_DEFAULT) { \
35461d06d6bSBaptiste Daroussin warnx("-%c: Conflicting option", (_ch)); \
35561d06d6bSBaptiste Daroussin goto usage; \
35661d06d6bSBaptiste Daroussin } while (/*CONSTCOND*/0)
35761d06d6bSBaptiste Daroussin
358c1c95addSBrooks Davis mparse_options = MPARSE_UTF8 | MPARSE_LATIN1 | MPARSE_VALIDATE;
35961d06d6bSBaptiste Daroussin path_arg = NULL;
36061d06d6bSBaptiste Daroussin op = OP_DEFAULT;
36161d06d6bSBaptiste Daroussin
3626d38604fSBaptiste Daroussin while ((ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")) != -1)
36361d06d6bSBaptiste Daroussin switch (ch) {
36461d06d6bSBaptiste Daroussin case 'a':
36561d06d6bSBaptiste Daroussin use_all = 1;
36661d06d6bSBaptiste Daroussin break;
36761d06d6bSBaptiste Daroussin case 'C':
36861d06d6bSBaptiste Daroussin CHECKOP(op, ch);
36961d06d6bSBaptiste Daroussin path_arg = optarg;
37061d06d6bSBaptiste Daroussin op = OP_CONFFILE;
37161d06d6bSBaptiste Daroussin break;
37261d06d6bSBaptiste Daroussin case 'D':
37361d06d6bSBaptiste Daroussin debug++;
37461d06d6bSBaptiste Daroussin break;
37561d06d6bSBaptiste Daroussin case 'd':
37661d06d6bSBaptiste Daroussin CHECKOP(op, ch);
37761d06d6bSBaptiste Daroussin path_arg = optarg;
37861d06d6bSBaptiste Daroussin op = OP_UPDATE;
37961d06d6bSBaptiste Daroussin break;
38061d06d6bSBaptiste Daroussin case 'n':
38161d06d6bSBaptiste Daroussin nodb = 1;
38261d06d6bSBaptiste Daroussin break;
38361d06d6bSBaptiste Daroussin case 'p':
38461d06d6bSBaptiste Daroussin warnings = 1;
38561d06d6bSBaptiste Daroussin break;
38661d06d6bSBaptiste Daroussin case 'Q':
38761d06d6bSBaptiste Daroussin mparse_options |= MPARSE_QUICK;
38861d06d6bSBaptiste Daroussin break;
38961d06d6bSBaptiste Daroussin case 'T':
3906d38604fSBaptiste Daroussin if (strcmp(optarg, "utf8") != 0) {
39161d06d6bSBaptiste Daroussin warnx("-T%s: Unsupported output format",
39261d06d6bSBaptiste Daroussin optarg);
39361d06d6bSBaptiste Daroussin goto usage;
39461d06d6bSBaptiste Daroussin }
39561d06d6bSBaptiste Daroussin write_utf8 = 1;
39661d06d6bSBaptiste Daroussin break;
39761d06d6bSBaptiste Daroussin case 't':
39861d06d6bSBaptiste Daroussin CHECKOP(op, ch);
39961d06d6bSBaptiste Daroussin dup2(STDOUT_FILENO, STDERR_FILENO);
40061d06d6bSBaptiste Daroussin op = OP_TEST;
40161d06d6bSBaptiste Daroussin nodb = warnings = 1;
40261d06d6bSBaptiste Daroussin break;
40361d06d6bSBaptiste Daroussin case 'u':
40461d06d6bSBaptiste Daroussin CHECKOP(op, ch);
40561d06d6bSBaptiste Daroussin path_arg = optarg;
40661d06d6bSBaptiste Daroussin op = OP_DELETE;
40761d06d6bSBaptiste Daroussin break;
40861d06d6bSBaptiste Daroussin case 'v':
40961d06d6bSBaptiste Daroussin /* Compatibility with espie@'s makewhatis. */
41061d06d6bSBaptiste Daroussin break;
41161d06d6bSBaptiste Daroussin default:
41261d06d6bSBaptiste Daroussin goto usage;
41361d06d6bSBaptiste Daroussin }
41461d06d6bSBaptiste Daroussin
41561d06d6bSBaptiste Daroussin argc -= optind;
41661d06d6bSBaptiste Daroussin argv += optind;
41761d06d6bSBaptiste Daroussin
41861d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
41961d06d6bSBaptiste Daroussin if (nodb) {
42061d06d6bSBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) {
42161d06d6bSBaptiste Daroussin warn("pledge");
42261d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_SYSERR;
42361d06d6bSBaptiste Daroussin }
42461d06d6bSBaptiste Daroussin }
42561d06d6bSBaptiste Daroussin #endif
42661d06d6bSBaptiste Daroussin
4276d38604fSBaptiste Daroussin if (op == OP_CONFFILE && argc > 0) {
42861d06d6bSBaptiste Daroussin warnx("-C: Too many arguments");
42961d06d6bSBaptiste Daroussin goto usage;
43061d06d6bSBaptiste Daroussin }
43161d06d6bSBaptiste Daroussin
43261d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_OK;
43361d06d6bSBaptiste Daroussin mchars_alloc();
4347295610fSBaptiste Daroussin mp = mparse_alloc(mparse_options, MANDOC_OS_OTHER, NULL);
43561d06d6bSBaptiste Daroussin mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev));
43661d06d6bSBaptiste Daroussin mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file));
43761d06d6bSBaptiste Daroussin
4386d38604fSBaptiste Daroussin if (op == OP_UPDATE || op == OP_DELETE || op == OP_TEST) {
43961d06d6bSBaptiste Daroussin
44061d06d6bSBaptiste Daroussin /*
44161d06d6bSBaptiste Daroussin * Most of these deal with a specific directory.
44261d06d6bSBaptiste Daroussin * Jump into that directory first.
44361d06d6bSBaptiste Daroussin */
4446d38604fSBaptiste Daroussin if (op != OP_TEST && set_basedir(path_arg, 1) == 0)
44561d06d6bSBaptiste Daroussin goto out;
44661d06d6bSBaptiste Daroussin
44761d06d6bSBaptiste Daroussin dba = nodb ? dba_new(128) : dba_read(MANDOC_DB);
44861d06d6bSBaptiste Daroussin if (dba != NULL) {
44961d06d6bSBaptiste Daroussin /*
45061d06d6bSBaptiste Daroussin * The existing database is usable. Process
45161d06d6bSBaptiste Daroussin * all files specified on the command-line.
45261d06d6bSBaptiste Daroussin */
45361d06d6bSBaptiste Daroussin use_all = 1;
45461d06d6bSBaptiste Daroussin for (i = 0; i < argc; i++)
45561d06d6bSBaptiste Daroussin filescan(argv[i]);
45661d06d6bSBaptiste Daroussin if (nodb == 0)
45761d06d6bSBaptiste Daroussin dbprune(dba);
45861d06d6bSBaptiste Daroussin } else {
45961d06d6bSBaptiste Daroussin /* Database missing or corrupt. */
46061d06d6bSBaptiste Daroussin if (op != OP_UPDATE || errno != ENOENT)
46161d06d6bSBaptiste Daroussin say(MANDOC_DB, "%s: Automatically recreating"
46261d06d6bSBaptiste Daroussin " from scratch", strerror(errno));
46361d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_OK;
46461d06d6bSBaptiste Daroussin op = OP_DEFAULT;
4656d38604fSBaptiste Daroussin if (treescan() == 0)
46661d06d6bSBaptiste Daroussin goto out;
46761d06d6bSBaptiste Daroussin dba = dba_new(128);
46861d06d6bSBaptiste Daroussin }
4696d38604fSBaptiste Daroussin if (op != OP_DELETE)
47061d06d6bSBaptiste Daroussin mpages_merge(dba, mp);
47161d06d6bSBaptiste Daroussin if (nodb == 0)
47261d06d6bSBaptiste Daroussin dbwrite(dba);
47361d06d6bSBaptiste Daroussin dba_free(dba);
47461d06d6bSBaptiste Daroussin } else {
47561d06d6bSBaptiste Daroussin /*
47661d06d6bSBaptiste Daroussin * If we have arguments, use them as our manpaths.
47761d06d6bSBaptiste Daroussin * If we don't, use man.conf(5).
47861d06d6bSBaptiste Daroussin */
47961d06d6bSBaptiste Daroussin if (argc > 0) {
48061d06d6bSBaptiste Daroussin conf.manpath.paths = mandoc_reallocarray(NULL,
48161d06d6bSBaptiste Daroussin argc, sizeof(char *));
48261d06d6bSBaptiste Daroussin conf.manpath.sz = (size_t)argc;
48361d06d6bSBaptiste Daroussin for (i = 0; i < argc; i++)
48461d06d6bSBaptiste Daroussin conf.manpath.paths[i] = mandoc_strdup(argv[i]);
48561d06d6bSBaptiste Daroussin } else
48661d06d6bSBaptiste Daroussin manconf_parse(&conf, path_arg, NULL, NULL);
48761d06d6bSBaptiste Daroussin
48861d06d6bSBaptiste Daroussin if (conf.manpath.sz == 0) {
48961d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
49061d06d6bSBaptiste Daroussin say("", "Empty manpath");
49161d06d6bSBaptiste Daroussin }
49261d06d6bSBaptiste Daroussin
49361d06d6bSBaptiste Daroussin /*
49461d06d6bSBaptiste Daroussin * First scan the tree rooted at a base directory, then
49561d06d6bSBaptiste Daroussin * build a new database and finally move it into place.
49661d06d6bSBaptiste Daroussin * Ignore zero-length directories and strip trailing
49761d06d6bSBaptiste Daroussin * slashes.
49861d06d6bSBaptiste Daroussin */
49961d06d6bSBaptiste Daroussin for (j = 0; j < conf.manpath.sz; j++) {
50061d06d6bSBaptiste Daroussin sz = strlen(conf.manpath.paths[j]);
50161d06d6bSBaptiste Daroussin if (sz && conf.manpath.paths[j][sz - 1] == '/')
50261d06d6bSBaptiste Daroussin conf.manpath.paths[j][--sz] = '\0';
5036d38604fSBaptiste Daroussin if (sz == 0)
50461d06d6bSBaptiste Daroussin continue;
50561d06d6bSBaptiste Daroussin
50661d06d6bSBaptiste Daroussin if (j) {
50761d06d6bSBaptiste Daroussin mandoc_ohash_init(&mpages, 6,
50861d06d6bSBaptiste Daroussin offsetof(struct mpage, inodev));
50961d06d6bSBaptiste Daroussin mandoc_ohash_init(&mlinks, 6,
51061d06d6bSBaptiste Daroussin offsetof(struct mlink, file));
51161d06d6bSBaptiste Daroussin }
51261d06d6bSBaptiste Daroussin
5136d38604fSBaptiste Daroussin if (set_basedir(conf.manpath.paths[j], argc > 0) == 0)
51461d06d6bSBaptiste Daroussin continue;
5156d38604fSBaptiste Daroussin if (treescan() == 0)
51661d06d6bSBaptiste Daroussin continue;
51761d06d6bSBaptiste Daroussin dba = dba_new(128);
51861d06d6bSBaptiste Daroussin mpages_merge(dba, mp);
51961d06d6bSBaptiste Daroussin if (nodb == 0)
52061d06d6bSBaptiste Daroussin dbwrite(dba);
52161d06d6bSBaptiste Daroussin dba_free(dba);
52261d06d6bSBaptiste Daroussin
52361d06d6bSBaptiste Daroussin if (j + 1 < conf.manpath.sz) {
52461d06d6bSBaptiste Daroussin mpages_free();
52561d06d6bSBaptiste Daroussin ohash_delete(&mpages);
52661d06d6bSBaptiste Daroussin ohash_delete(&mlinks);
52761d06d6bSBaptiste Daroussin }
52861d06d6bSBaptiste Daroussin }
52961d06d6bSBaptiste Daroussin }
53061d06d6bSBaptiste Daroussin out:
53161d06d6bSBaptiste Daroussin manconf_free(&conf);
53261d06d6bSBaptiste Daroussin mparse_free(mp);
53361d06d6bSBaptiste Daroussin mchars_free();
53461d06d6bSBaptiste Daroussin mpages_free();
53561d06d6bSBaptiste Daroussin ohash_delete(&mpages);
53661d06d6bSBaptiste Daroussin ohash_delete(&mlinks);
537c1c95addSBrooks Davis #if DEBUG_MEMORY
538c1c95addSBrooks Davis mandoc_dbg_finish();
539c1c95addSBrooks Davis #endif
54061d06d6bSBaptiste Daroussin return exitcode;
54161d06d6bSBaptiste Daroussin usage:
54261d06d6bSBaptiste Daroussin progname = getprogname();
54361d06d6bSBaptiste Daroussin fprintf(stderr, "usage: %s [-aDnpQ] [-C file] [-Tutf8]\n"
54461d06d6bSBaptiste Daroussin " %s [-aDnpQ] [-Tutf8] dir ...\n"
54561d06d6bSBaptiste Daroussin " %s [-DnpQ] [-Tutf8] -d dir [file ...]\n"
54661d06d6bSBaptiste Daroussin " %s [-Dnp] -u dir [file ...]\n"
54761d06d6bSBaptiste Daroussin " %s [-Q] -t file ...\n",
54861d06d6bSBaptiste Daroussin progname, progname, progname, progname, progname);
54961d06d6bSBaptiste Daroussin
55061d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG;
55161d06d6bSBaptiste Daroussin }
55261d06d6bSBaptiste Daroussin
55361d06d6bSBaptiste Daroussin /*
55461d06d6bSBaptiste Daroussin * To get a singly linked list in alpha order while inserting entries
55561d06d6bSBaptiste Daroussin * at the beginning, process directory entries in reverse alpha order.
55661d06d6bSBaptiste Daroussin */
55761d06d6bSBaptiste Daroussin static int
55861d06d6bSBaptiste Daroussin #if HAVE_FTS_COMPARE_CONST
fts_compare(const FTSENT * const * a,const FTSENT * const * b)55961d06d6bSBaptiste Daroussin fts_compare(const FTSENT *const *a, const FTSENT *const *b)
56061d06d6bSBaptiste Daroussin #else
56161d06d6bSBaptiste Daroussin fts_compare(const FTSENT **a, const FTSENT **b)
56261d06d6bSBaptiste Daroussin #endif
56361d06d6bSBaptiste Daroussin {
56461d06d6bSBaptiste Daroussin return -strcmp((*a)->fts_name, (*b)->fts_name);
56561d06d6bSBaptiste Daroussin }
56661d06d6bSBaptiste Daroussin
56761d06d6bSBaptiste Daroussin /*
56861d06d6bSBaptiste Daroussin * Scan a directory tree rooted at "basedir" for manpages.
56961d06d6bSBaptiste Daroussin * We use fts(), scanning directory parts along the way for clues to our
57061d06d6bSBaptiste Daroussin * section and architecture.
57161d06d6bSBaptiste Daroussin *
57261d06d6bSBaptiste Daroussin * If use_all has been specified, grok all files.
57361d06d6bSBaptiste Daroussin * If not, sanitise paths to the following:
57461d06d6bSBaptiste Daroussin *
57561d06d6bSBaptiste Daroussin * [./]man*[/<arch>]/<name>.<section>
57661d06d6bSBaptiste Daroussin * or
57761d06d6bSBaptiste Daroussin * [./]cat<section>[/<arch>]/<name>.0
57861d06d6bSBaptiste Daroussin *
57961d06d6bSBaptiste Daroussin * TODO: accommodate for multi-language directories.
58061d06d6bSBaptiste Daroussin */
58161d06d6bSBaptiste Daroussin static int
treescan(void)58261d06d6bSBaptiste Daroussin treescan(void)
58361d06d6bSBaptiste Daroussin {
58461d06d6bSBaptiste Daroussin char buf[PATH_MAX];
58561d06d6bSBaptiste Daroussin FTS *f;
58661d06d6bSBaptiste Daroussin FTSENT *ff;
58761d06d6bSBaptiste Daroussin struct mlink *mlink;
58861d06d6bSBaptiste Daroussin int gzip;
58961d06d6bSBaptiste Daroussin enum form dform;
59061d06d6bSBaptiste Daroussin char *dsec, *arch, *fsec, *cp;
59161d06d6bSBaptiste Daroussin const char *path;
59261d06d6bSBaptiste Daroussin const char *argv[2];
59361d06d6bSBaptiste Daroussin
59461d06d6bSBaptiste Daroussin argv[0] = ".";
59561d06d6bSBaptiste Daroussin argv[1] = NULL;
59661d06d6bSBaptiste Daroussin
59761d06d6bSBaptiste Daroussin f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR,
59861d06d6bSBaptiste Daroussin fts_compare);
59961d06d6bSBaptiste Daroussin if (f == NULL) {
60061d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
60161d06d6bSBaptiste Daroussin say("", "&fts_open");
60261d06d6bSBaptiste Daroussin return 0;
60361d06d6bSBaptiste Daroussin }
60461d06d6bSBaptiste Daroussin
60561d06d6bSBaptiste Daroussin dsec = arch = NULL;
60661d06d6bSBaptiste Daroussin dform = FORM_NONE;
60761d06d6bSBaptiste Daroussin
60861d06d6bSBaptiste Daroussin while ((ff = fts_read(f)) != NULL) {
60961d06d6bSBaptiste Daroussin path = ff->fts_path + 2;
61061d06d6bSBaptiste Daroussin switch (ff->fts_info) {
61161d06d6bSBaptiste Daroussin
61261d06d6bSBaptiste Daroussin /*
61361d06d6bSBaptiste Daroussin * Symbolic links require various sanity checks,
61461d06d6bSBaptiste Daroussin * then get handled just like regular files.
61561d06d6bSBaptiste Daroussin */
61661d06d6bSBaptiste Daroussin case FTS_SL:
61761d06d6bSBaptiste Daroussin if (realpath(path, buf) == NULL) {
61861d06d6bSBaptiste Daroussin if (warnings)
61961d06d6bSBaptiste Daroussin say(path, "&realpath");
62061d06d6bSBaptiste Daroussin continue;
62161d06d6bSBaptiste Daroussin }
6226d38604fSBaptiste Daroussin if (strncmp(buf, basedir, basedir_len) != 0
6236d38604fSBaptiste Daroussin #ifdef READ_ALLOWED_PATH
6246d38604fSBaptiste Daroussin && !read_allowed(buf)
62561d06d6bSBaptiste Daroussin #endif
62661d06d6bSBaptiste Daroussin ) {
62761d06d6bSBaptiste Daroussin if (warnings) say("",
62861d06d6bSBaptiste Daroussin "%s: outside base directory", buf);
62961d06d6bSBaptiste Daroussin continue;
63061d06d6bSBaptiste Daroussin }
63161d06d6bSBaptiste Daroussin /* Use logical inode to avoid mpages dupe. */
63261d06d6bSBaptiste Daroussin if (stat(path, ff->fts_statp) == -1) {
63361d06d6bSBaptiste Daroussin if (warnings)
63461d06d6bSBaptiste Daroussin say(path, "&stat");
63561d06d6bSBaptiste Daroussin continue;
63661d06d6bSBaptiste Daroussin }
6376d38604fSBaptiste Daroussin if ((ff->fts_statp->st_mode & S_IFMT) != S_IFREG)
6386d38604fSBaptiste Daroussin continue;
63961d06d6bSBaptiste Daroussin /* FALLTHROUGH */
64061d06d6bSBaptiste Daroussin
64161d06d6bSBaptiste Daroussin /*
64261d06d6bSBaptiste Daroussin * If we're a regular file, add an mlink by using the
64361d06d6bSBaptiste Daroussin * stored directory data and handling the filename.
64461d06d6bSBaptiste Daroussin */
64561d06d6bSBaptiste Daroussin case FTS_F:
64661d06d6bSBaptiste Daroussin if ( ! strcmp(path, MANDOC_DB))
64761d06d6bSBaptiste Daroussin continue;
64861d06d6bSBaptiste Daroussin if ( ! use_all && ff->fts_level < 2) {
64961d06d6bSBaptiste Daroussin if (warnings)
65061d06d6bSBaptiste Daroussin say(path, "Extraneous file");
65161d06d6bSBaptiste Daroussin continue;
65261d06d6bSBaptiste Daroussin }
65361d06d6bSBaptiste Daroussin gzip = 0;
65461d06d6bSBaptiste Daroussin fsec = NULL;
65561d06d6bSBaptiste Daroussin while (fsec == NULL) {
65661d06d6bSBaptiste Daroussin fsec = strrchr(ff->fts_name, '.');
65761d06d6bSBaptiste Daroussin if (fsec == NULL || strcmp(fsec+1, "gz"))
65861d06d6bSBaptiste Daroussin break;
65961d06d6bSBaptiste Daroussin gzip = 1;
66061d06d6bSBaptiste Daroussin *fsec = '\0';
66161d06d6bSBaptiste Daroussin fsec = NULL;
66261d06d6bSBaptiste Daroussin }
66361d06d6bSBaptiste Daroussin if (fsec == NULL) {
66461d06d6bSBaptiste Daroussin if ( ! use_all) {
66561d06d6bSBaptiste Daroussin if (warnings)
66661d06d6bSBaptiste Daroussin say(path,
66761d06d6bSBaptiste Daroussin "No filename suffix");
66861d06d6bSBaptiste Daroussin continue;
66961d06d6bSBaptiste Daroussin }
67061d06d6bSBaptiste Daroussin } else if ( ! strcmp(++fsec, "html")) {
67161d06d6bSBaptiste Daroussin if (warnings)
67261d06d6bSBaptiste Daroussin say(path, "Skip html");
67361d06d6bSBaptiste Daroussin continue;
67461d06d6bSBaptiste Daroussin } else if ( ! strcmp(fsec, "ps")) {
67561d06d6bSBaptiste Daroussin if (warnings)
67661d06d6bSBaptiste Daroussin say(path, "Skip ps");
67761d06d6bSBaptiste Daroussin continue;
67861d06d6bSBaptiste Daroussin } else if ( ! strcmp(fsec, "pdf")) {
67961d06d6bSBaptiste Daroussin if (warnings)
68061d06d6bSBaptiste Daroussin say(path, "Skip pdf");
68161d06d6bSBaptiste Daroussin continue;
68261d06d6bSBaptiste Daroussin } else if ( ! use_all &&
68361d06d6bSBaptiste Daroussin ((dform == FORM_SRC &&
68461d06d6bSBaptiste Daroussin strncmp(fsec, dsec, strlen(dsec))) ||
68561d06d6bSBaptiste Daroussin (dform == FORM_CAT && strcmp(fsec, "0")))) {
68661d06d6bSBaptiste Daroussin if (warnings)
68761d06d6bSBaptiste Daroussin say(path, "Wrong filename suffix");
68861d06d6bSBaptiste Daroussin continue;
68961d06d6bSBaptiste Daroussin } else
69061d06d6bSBaptiste Daroussin fsec[-1] = '\0';
69161d06d6bSBaptiste Daroussin
69261d06d6bSBaptiste Daroussin mlink = mandoc_calloc(1, sizeof(struct mlink));
69361d06d6bSBaptiste Daroussin if (strlcpy(mlink->file, path,
69461d06d6bSBaptiste Daroussin sizeof(mlink->file)) >=
69561d06d6bSBaptiste Daroussin sizeof(mlink->file)) {
69661d06d6bSBaptiste Daroussin say(path, "Filename too long");
69761d06d6bSBaptiste Daroussin free(mlink);
69861d06d6bSBaptiste Daroussin continue;
69961d06d6bSBaptiste Daroussin }
70061d06d6bSBaptiste Daroussin mlink->dform = dform;
70161d06d6bSBaptiste Daroussin mlink->dsec = dsec;
70261d06d6bSBaptiste Daroussin mlink->arch = arch;
70361d06d6bSBaptiste Daroussin mlink->name = ff->fts_name;
70461d06d6bSBaptiste Daroussin mlink->fsec = fsec;
70561d06d6bSBaptiste Daroussin mlink->gzip = gzip;
70661d06d6bSBaptiste Daroussin mlink_add(mlink, ff->fts_statp);
70761d06d6bSBaptiste Daroussin continue;
70861d06d6bSBaptiste Daroussin
70961d06d6bSBaptiste Daroussin case FTS_D:
71061d06d6bSBaptiste Daroussin case FTS_DP:
71161d06d6bSBaptiste Daroussin break;
71261d06d6bSBaptiste Daroussin
71361d06d6bSBaptiste Daroussin default:
71461d06d6bSBaptiste Daroussin if (warnings)
71561d06d6bSBaptiste Daroussin say(path, "Not a regular file");
71661d06d6bSBaptiste Daroussin continue;
71761d06d6bSBaptiste Daroussin }
71861d06d6bSBaptiste Daroussin
71961d06d6bSBaptiste Daroussin switch (ff->fts_level) {
72061d06d6bSBaptiste Daroussin case 0:
72161d06d6bSBaptiste Daroussin /* Ignore the root directory. */
72261d06d6bSBaptiste Daroussin break;
72361d06d6bSBaptiste Daroussin case 1:
72461d06d6bSBaptiste Daroussin /*
72561d06d6bSBaptiste Daroussin * This might contain manX/ or catX/.
72661d06d6bSBaptiste Daroussin * Try to infer this from the name.
72761d06d6bSBaptiste Daroussin * If we're not in use_all, enforce it.
72861d06d6bSBaptiste Daroussin */
72961d06d6bSBaptiste Daroussin cp = ff->fts_name;
73061d06d6bSBaptiste Daroussin if (ff->fts_info == FTS_DP) {
73161d06d6bSBaptiste Daroussin dform = FORM_NONE;
73261d06d6bSBaptiste Daroussin dsec = NULL;
73361d06d6bSBaptiste Daroussin break;
73461d06d6bSBaptiste Daroussin }
73561d06d6bSBaptiste Daroussin
73661d06d6bSBaptiste Daroussin if ( ! strncmp(cp, "man", 3)) {
73761d06d6bSBaptiste Daroussin dform = FORM_SRC;
73861d06d6bSBaptiste Daroussin dsec = cp + 3;
73961d06d6bSBaptiste Daroussin } else if ( ! strncmp(cp, "cat", 3)) {
74061d06d6bSBaptiste Daroussin dform = FORM_CAT;
74161d06d6bSBaptiste Daroussin dsec = cp + 3;
74261d06d6bSBaptiste Daroussin } else {
74361d06d6bSBaptiste Daroussin dform = FORM_NONE;
74461d06d6bSBaptiste Daroussin dsec = NULL;
74561d06d6bSBaptiste Daroussin }
74661d06d6bSBaptiste Daroussin
74761d06d6bSBaptiste Daroussin if (dsec != NULL || use_all)
74861d06d6bSBaptiste Daroussin break;
74961d06d6bSBaptiste Daroussin
75061d06d6bSBaptiste Daroussin if (warnings)
75161d06d6bSBaptiste Daroussin say(path, "Unknown directory part");
75261d06d6bSBaptiste Daroussin fts_set(f, ff, FTS_SKIP);
75361d06d6bSBaptiste Daroussin break;
75461d06d6bSBaptiste Daroussin case 2:
75561d06d6bSBaptiste Daroussin /*
75661d06d6bSBaptiste Daroussin * Possibly our architecture.
75761d06d6bSBaptiste Daroussin * If we're descending, keep tabs on it.
75861d06d6bSBaptiste Daroussin */
75961d06d6bSBaptiste Daroussin if (ff->fts_info != FTS_DP && dsec != NULL)
76061d06d6bSBaptiste Daroussin arch = ff->fts_name;
76161d06d6bSBaptiste Daroussin else
76261d06d6bSBaptiste Daroussin arch = NULL;
76361d06d6bSBaptiste Daroussin break;
76461d06d6bSBaptiste Daroussin default:
76561d06d6bSBaptiste Daroussin if (ff->fts_info == FTS_DP || use_all)
76661d06d6bSBaptiste Daroussin break;
76761d06d6bSBaptiste Daroussin if (warnings)
76861d06d6bSBaptiste Daroussin say(path, "Extraneous directory part");
76961d06d6bSBaptiste Daroussin fts_set(f, ff, FTS_SKIP);
77061d06d6bSBaptiste Daroussin break;
77161d06d6bSBaptiste Daroussin }
77261d06d6bSBaptiste Daroussin }
77361d06d6bSBaptiste Daroussin
77461d06d6bSBaptiste Daroussin fts_close(f);
77561d06d6bSBaptiste Daroussin return 1;
77661d06d6bSBaptiste Daroussin }
77761d06d6bSBaptiste Daroussin
77861d06d6bSBaptiste Daroussin /*
77961d06d6bSBaptiste Daroussin * Add a file to the mlinks table.
78061d06d6bSBaptiste Daroussin * Do not verify that it's a "valid" looking manpage (we'll do that
78161d06d6bSBaptiste Daroussin * later).
78261d06d6bSBaptiste Daroussin *
78361d06d6bSBaptiste Daroussin * Try to infer the manual section, architecture, and page name from the
78461d06d6bSBaptiste Daroussin * path, assuming it looks like
78561d06d6bSBaptiste Daroussin *
78661d06d6bSBaptiste Daroussin * [./]man*[/<arch>]/<name>.<section>
78761d06d6bSBaptiste Daroussin * or
78861d06d6bSBaptiste Daroussin * [./]cat<section>[/<arch>]/<name>.0
78961d06d6bSBaptiste Daroussin *
79061d06d6bSBaptiste Daroussin * See treescan() for the fts(3) version of this.
79161d06d6bSBaptiste Daroussin */
79261d06d6bSBaptiste Daroussin static void
filescan(const char * infile)7936d38604fSBaptiste Daroussin filescan(const char *infile)
79461d06d6bSBaptiste Daroussin {
79561d06d6bSBaptiste Daroussin struct stat st;
79661d06d6bSBaptiste Daroussin struct mlink *mlink;
7976d38604fSBaptiste Daroussin char *linkfile, *p, *realdir, *start, *usefile;
7986d38604fSBaptiste Daroussin size_t realdir_len;
79961d06d6bSBaptiste Daroussin
80061d06d6bSBaptiste Daroussin assert(use_all);
80161d06d6bSBaptiste Daroussin
8026d38604fSBaptiste Daroussin if (strncmp(infile, "./", 2) == 0)
8036d38604fSBaptiste Daroussin infile += 2;
80461d06d6bSBaptiste Daroussin
80561d06d6bSBaptiste Daroussin /*
80661d06d6bSBaptiste Daroussin * We have to do lstat(2) before realpath(3) loses
80761d06d6bSBaptiste Daroussin * the information whether this is a symbolic link.
80861d06d6bSBaptiste Daroussin * We need to know that because for symbolic links,
809c1c95addSBrooks Davis * we want to use the original file name, while for
81061d06d6bSBaptiste Daroussin * regular files, we want to use the real path.
81161d06d6bSBaptiste Daroussin */
8126d38604fSBaptiste Daroussin if (lstat(infile, &st) == -1) {
81361d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8146d38604fSBaptiste Daroussin say(infile, "&lstat");
81561d06d6bSBaptiste Daroussin return;
8166d38604fSBaptiste Daroussin } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) {
81761d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8186d38604fSBaptiste Daroussin say(infile, "Not a regular file");
81961d06d6bSBaptiste Daroussin return;
82061d06d6bSBaptiste Daroussin }
82161d06d6bSBaptiste Daroussin
82261d06d6bSBaptiste Daroussin /*
82361d06d6bSBaptiste Daroussin * We have to resolve the file name to the real path
82461d06d6bSBaptiste Daroussin * in any case for the base directory check.
82561d06d6bSBaptiste Daroussin */
8266d38604fSBaptiste Daroussin if ((usefile = realpath(infile, NULL)) == NULL) {
82761d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8286d38604fSBaptiste Daroussin say(infile, "&realpath");
82961d06d6bSBaptiste Daroussin return;
83061d06d6bSBaptiste Daroussin }
83161d06d6bSBaptiste Daroussin
8326d38604fSBaptiste Daroussin if (op == OP_TEST)
8336d38604fSBaptiste Daroussin start = usefile;
8346d38604fSBaptiste Daroussin else if (strncmp(usefile, basedir, basedir_len) == 0)
8356d38604fSBaptiste Daroussin start = usefile + basedir_len;
8366d38604fSBaptiste Daroussin #ifdef READ_ALLOWED_PATH
8376d38604fSBaptiste Daroussin else if (read_allowed(usefile))
8386d38604fSBaptiste Daroussin start = usefile;
83961d06d6bSBaptiste Daroussin #endif
84061d06d6bSBaptiste Daroussin else {
84161d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8426d38604fSBaptiste Daroussin say("", "%s: outside base directory", infile);
8436d38604fSBaptiste Daroussin free(usefile);
84461d06d6bSBaptiste Daroussin return;
84561d06d6bSBaptiste Daroussin }
84661d06d6bSBaptiste Daroussin
84761d06d6bSBaptiste Daroussin /*
84861d06d6bSBaptiste Daroussin * Now we are sure the file is inside our tree.
84961d06d6bSBaptiste Daroussin * If it is a symbolic link, ignore the real path
85061d06d6bSBaptiste Daroussin * and use the original name.
85161d06d6bSBaptiste Daroussin */
8526d38604fSBaptiste Daroussin do {
8536d38604fSBaptiste Daroussin if (S_ISLNK(st.st_mode) == 0)
8546d38604fSBaptiste Daroussin break;
8556d38604fSBaptiste Daroussin
8566d38604fSBaptiste Daroussin /*
8576d38604fSBaptiste Daroussin * Some implementations of realpath(3) may succeed
8586d38604fSBaptiste Daroussin * even if the target of the link does not exist,
8596d38604fSBaptiste Daroussin * so check again for extra safety.
8606d38604fSBaptiste Daroussin */
8616d38604fSBaptiste Daroussin if (stat(usefile, &st) == -1) {
86261d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8636d38604fSBaptiste Daroussin say(infile, "&stat");
8646d38604fSBaptiste Daroussin free(usefile);
86561d06d6bSBaptiste Daroussin return;
86661d06d6bSBaptiste Daroussin }
8676d38604fSBaptiste Daroussin linkfile = mandoc_strdup(infile);
8686d38604fSBaptiste Daroussin if (op == OP_TEST) {
8696d38604fSBaptiste Daroussin free(usefile);
8706d38604fSBaptiste Daroussin start = usefile = linkfile;
8716d38604fSBaptiste Daroussin break;
8726d38604fSBaptiste Daroussin }
8736d38604fSBaptiste Daroussin if (strncmp(infile, basedir, basedir_len) == 0) {
8746d38604fSBaptiste Daroussin free(usefile);
8756d38604fSBaptiste Daroussin usefile = linkfile;
8766d38604fSBaptiste Daroussin start = usefile + basedir_len;
8776d38604fSBaptiste Daroussin break;
8786d38604fSBaptiste Daroussin }
8796d38604fSBaptiste Daroussin
8806d38604fSBaptiste Daroussin /*
8816d38604fSBaptiste Daroussin * This symbolic link points into the basedir
8826d38604fSBaptiste Daroussin * from the outside. Let's see whether any of
8836d38604fSBaptiste Daroussin * the parent directories resolve to the basedir.
8846d38604fSBaptiste Daroussin */
8856d38604fSBaptiste Daroussin p = strchr(linkfile, '\0');
8866d38604fSBaptiste Daroussin do {
8876d38604fSBaptiste Daroussin while (*--p != '/')
8886d38604fSBaptiste Daroussin continue;
8896d38604fSBaptiste Daroussin *p = '\0';
8906d38604fSBaptiste Daroussin if ((realdir = realpath(linkfile, NULL)) == NULL) {
8916d38604fSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
8926d38604fSBaptiste Daroussin say(infile, "&realpath");
8936d38604fSBaptiste Daroussin free(linkfile);
8946d38604fSBaptiste Daroussin free(usefile);
89561d06d6bSBaptiste Daroussin return;
89661d06d6bSBaptiste Daroussin }
8976d38604fSBaptiste Daroussin realdir_len = strlen(realdir) + 1;
8986d38604fSBaptiste Daroussin free(realdir);
8996d38604fSBaptiste Daroussin *p = '/';
9006d38604fSBaptiste Daroussin } while (realdir_len > basedir_len);
9016d38604fSBaptiste Daroussin
9026d38604fSBaptiste Daroussin /*
9036d38604fSBaptiste Daroussin * If one of the directories resolves to the basedir,
9046d38604fSBaptiste Daroussin * use the rest of the original name.
9056d38604fSBaptiste Daroussin * Otherwise, the best we can do
9066d38604fSBaptiste Daroussin * is to use the filename pointed to.
9076d38604fSBaptiste Daroussin */
9086d38604fSBaptiste Daroussin if (realdir_len == basedir_len) {
9096d38604fSBaptiste Daroussin free(usefile);
9106d38604fSBaptiste Daroussin usefile = linkfile;
9116d38604fSBaptiste Daroussin start = p + 1;
9126d38604fSBaptiste Daroussin } else {
9136d38604fSBaptiste Daroussin free(linkfile);
9146d38604fSBaptiste Daroussin start = usefile + basedir_len;
91561d06d6bSBaptiste Daroussin }
9166d38604fSBaptiste Daroussin } while (/* CONSTCOND */ 0);
91761d06d6bSBaptiste Daroussin
91861d06d6bSBaptiste Daroussin mlink = mandoc_calloc(1, sizeof(struct mlink));
91961d06d6bSBaptiste Daroussin mlink->dform = FORM_NONE;
92061d06d6bSBaptiste Daroussin if (strlcpy(mlink->file, start, sizeof(mlink->file)) >=
92161d06d6bSBaptiste Daroussin sizeof(mlink->file)) {
92261d06d6bSBaptiste Daroussin say(start, "Filename too long");
92361d06d6bSBaptiste Daroussin free(mlink);
9246d38604fSBaptiste Daroussin free(usefile);
92561d06d6bSBaptiste Daroussin return;
92661d06d6bSBaptiste Daroussin }
92761d06d6bSBaptiste Daroussin
92861d06d6bSBaptiste Daroussin /*
92961d06d6bSBaptiste Daroussin * In test mode or when the original name is absolute
93061d06d6bSBaptiste Daroussin * but outside our tree, guess the base directory.
93161d06d6bSBaptiste Daroussin */
93261d06d6bSBaptiste Daroussin
9336d38604fSBaptiste Daroussin if (op == OP_TEST || (start == usefile && *start == '/')) {
9346d38604fSBaptiste Daroussin if (strncmp(usefile, "man/", 4) == 0)
9356d38604fSBaptiste Daroussin start = usefile + 4;
9366d38604fSBaptiste Daroussin else if ((start = strstr(usefile, "/man/")) != NULL)
93761d06d6bSBaptiste Daroussin start += 5;
93861d06d6bSBaptiste Daroussin else
9396d38604fSBaptiste Daroussin start = usefile;
94061d06d6bSBaptiste Daroussin }
94161d06d6bSBaptiste Daroussin
94261d06d6bSBaptiste Daroussin /*
94361d06d6bSBaptiste Daroussin * First try to guess our directory structure.
94461d06d6bSBaptiste Daroussin * If we find a separator, try to look for man* or cat*.
94561d06d6bSBaptiste Daroussin * If we find one of these and what's underneath is a directory,
94661d06d6bSBaptiste Daroussin * assume it's an architecture.
94761d06d6bSBaptiste Daroussin */
9486d38604fSBaptiste Daroussin if ((p = strchr(start, '/')) != NULL) {
94961d06d6bSBaptiste Daroussin *p++ = '\0';
9506d38604fSBaptiste Daroussin if (strncmp(start, "man", 3) == 0) {
95161d06d6bSBaptiste Daroussin mlink->dform = FORM_SRC;
95261d06d6bSBaptiste Daroussin mlink->dsec = start + 3;
9536d38604fSBaptiste Daroussin } else if (strncmp(start, "cat", 3) == 0) {
95461d06d6bSBaptiste Daroussin mlink->dform = FORM_CAT;
95561d06d6bSBaptiste Daroussin mlink->dsec = start + 3;
95661d06d6bSBaptiste Daroussin }
95761d06d6bSBaptiste Daroussin
95861d06d6bSBaptiste Daroussin start = p;
9596d38604fSBaptiste Daroussin if (mlink->dsec != NULL && (p = strchr(start, '/')) != NULL) {
96061d06d6bSBaptiste Daroussin *p++ = '\0';
96161d06d6bSBaptiste Daroussin mlink->arch = start;
96261d06d6bSBaptiste Daroussin start = p;
96361d06d6bSBaptiste Daroussin }
96461d06d6bSBaptiste Daroussin }
96561d06d6bSBaptiste Daroussin
96661d06d6bSBaptiste Daroussin /*
96761d06d6bSBaptiste Daroussin * Now check the file suffix.
96861d06d6bSBaptiste Daroussin * Suffix of `.0' indicates a catpage, `.1-9' is a manpage.
96961d06d6bSBaptiste Daroussin */
97061d06d6bSBaptiste Daroussin p = strrchr(start, '\0');
9716d38604fSBaptiste Daroussin while (p-- > start && *p != '/' && *p != '.')
9726d38604fSBaptiste Daroussin continue;
97361d06d6bSBaptiste Daroussin
9746d38604fSBaptiste Daroussin if (*p == '.') {
97561d06d6bSBaptiste Daroussin *p++ = '\0';
97661d06d6bSBaptiste Daroussin mlink->fsec = p;
97761d06d6bSBaptiste Daroussin }
97861d06d6bSBaptiste Daroussin
97961d06d6bSBaptiste Daroussin /*
98061d06d6bSBaptiste Daroussin * Now try to parse the name.
98161d06d6bSBaptiste Daroussin * Use the filename portion of the path.
98261d06d6bSBaptiste Daroussin */
98361d06d6bSBaptiste Daroussin mlink->name = start;
9846d38604fSBaptiste Daroussin if ((p = strrchr(start, '/')) != NULL) {
98561d06d6bSBaptiste Daroussin mlink->name = p + 1;
98661d06d6bSBaptiste Daroussin *p = '\0';
98761d06d6bSBaptiste Daroussin }
98861d06d6bSBaptiste Daroussin mlink_add(mlink, &st);
9896d38604fSBaptiste Daroussin free(usefile);
99061d06d6bSBaptiste Daroussin }
99161d06d6bSBaptiste Daroussin
99261d06d6bSBaptiste Daroussin static void
mlink_add(struct mlink * mlink,const struct stat * st)99361d06d6bSBaptiste Daroussin mlink_add(struct mlink *mlink, const struct stat *st)
99461d06d6bSBaptiste Daroussin {
99561d06d6bSBaptiste Daroussin struct inodev inodev;
99661d06d6bSBaptiste Daroussin struct mpage *mpage;
99761d06d6bSBaptiste Daroussin unsigned int slot;
99861d06d6bSBaptiste Daroussin
99961d06d6bSBaptiste Daroussin assert(NULL != mlink->file);
100061d06d6bSBaptiste Daroussin
100161d06d6bSBaptiste Daroussin mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : "");
100261d06d6bSBaptiste Daroussin mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : "");
100361d06d6bSBaptiste Daroussin mlink->name = mandoc_strdup(mlink->name ? mlink->name : "");
100461d06d6bSBaptiste Daroussin mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : "");
100561d06d6bSBaptiste Daroussin
100661d06d6bSBaptiste Daroussin if ('0' == *mlink->fsec) {
100761d06d6bSBaptiste Daroussin free(mlink->fsec);
100861d06d6bSBaptiste Daroussin mlink->fsec = mandoc_strdup(mlink->dsec);
100961d06d6bSBaptiste Daroussin mlink->fform = FORM_CAT;
101061d06d6bSBaptiste Daroussin } else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec)
101161d06d6bSBaptiste Daroussin mlink->fform = FORM_SRC;
101261d06d6bSBaptiste Daroussin else
101361d06d6bSBaptiste Daroussin mlink->fform = FORM_NONE;
101461d06d6bSBaptiste Daroussin
101561d06d6bSBaptiste Daroussin slot = ohash_qlookup(&mlinks, mlink->file);
101661d06d6bSBaptiste Daroussin assert(NULL == ohash_find(&mlinks, slot));
101761d06d6bSBaptiste Daroussin ohash_insert(&mlinks, slot, mlink);
101861d06d6bSBaptiste Daroussin
101961d06d6bSBaptiste Daroussin memset(&inodev, 0, sizeof(inodev)); /* Clear padding. */
102061d06d6bSBaptiste Daroussin inodev.st_ino = st->st_ino;
102161d06d6bSBaptiste Daroussin inodev.st_dev = st->st_dev;
102261d06d6bSBaptiste Daroussin slot = ohash_lookup_memory(&mpages, (char *)&inodev,
102361d06d6bSBaptiste Daroussin sizeof(struct inodev), inodev.st_ino);
102461d06d6bSBaptiste Daroussin mpage = ohash_find(&mpages, slot);
102561d06d6bSBaptiste Daroussin if (NULL == mpage) {
102661d06d6bSBaptiste Daroussin mpage = mandoc_calloc(1, sizeof(struct mpage));
102761d06d6bSBaptiste Daroussin mpage->inodev.st_ino = inodev.st_ino;
102861d06d6bSBaptiste Daroussin mpage->inodev.st_dev = inodev.st_dev;
102961d06d6bSBaptiste Daroussin mpage->form = FORM_NONE;
103061d06d6bSBaptiste Daroussin mpage->next = mpage_head;
103161d06d6bSBaptiste Daroussin mpage_head = mpage;
103261d06d6bSBaptiste Daroussin ohash_insert(&mpages, slot, mpage);
103361d06d6bSBaptiste Daroussin } else
103461d06d6bSBaptiste Daroussin mlink->next = mpage->mlinks;
103561d06d6bSBaptiste Daroussin mpage->mlinks = mlink;
103661d06d6bSBaptiste Daroussin mlink->mpage = mpage;
103761d06d6bSBaptiste Daroussin }
103861d06d6bSBaptiste Daroussin
103961d06d6bSBaptiste Daroussin static void
mlink_free(struct mlink * mlink)104061d06d6bSBaptiste Daroussin mlink_free(struct mlink *mlink)
104161d06d6bSBaptiste Daroussin {
104261d06d6bSBaptiste Daroussin
104361d06d6bSBaptiste Daroussin free(mlink->dsec);
104461d06d6bSBaptiste Daroussin free(mlink->arch);
104561d06d6bSBaptiste Daroussin free(mlink->name);
104661d06d6bSBaptiste Daroussin free(mlink->fsec);
104761d06d6bSBaptiste Daroussin free(mlink);
104861d06d6bSBaptiste Daroussin }
104961d06d6bSBaptiste Daroussin
105061d06d6bSBaptiste Daroussin static void
mpages_free(void)105161d06d6bSBaptiste Daroussin mpages_free(void)
105261d06d6bSBaptiste Daroussin {
105361d06d6bSBaptiste Daroussin struct mpage *mpage;
105461d06d6bSBaptiste Daroussin struct mlink *mlink;
105561d06d6bSBaptiste Daroussin
105661d06d6bSBaptiste Daroussin while ((mpage = mpage_head) != NULL) {
105761d06d6bSBaptiste Daroussin while ((mlink = mpage->mlinks) != NULL) {
105861d06d6bSBaptiste Daroussin mpage->mlinks = mlink->next;
105961d06d6bSBaptiste Daroussin mlink_free(mlink);
106061d06d6bSBaptiste Daroussin }
106161d06d6bSBaptiste Daroussin mpage_head = mpage->next;
106261d06d6bSBaptiste Daroussin free(mpage->sec);
106361d06d6bSBaptiste Daroussin free(mpage->arch);
106461d06d6bSBaptiste Daroussin free(mpage->title);
106561d06d6bSBaptiste Daroussin free(mpage->desc);
106661d06d6bSBaptiste Daroussin free(mpage);
106761d06d6bSBaptiste Daroussin }
106861d06d6bSBaptiste Daroussin }
106961d06d6bSBaptiste Daroussin
107061d06d6bSBaptiste Daroussin /*
107161d06d6bSBaptiste Daroussin * For each mlink to the mpage, check whether the path looks like
107261d06d6bSBaptiste Daroussin * it is formatted, and if it does, check whether a source manual
107361d06d6bSBaptiste Daroussin * exists by the same name, ignoring the suffix.
107461d06d6bSBaptiste Daroussin * If both conditions hold, drop the mlink.
107561d06d6bSBaptiste Daroussin */
107661d06d6bSBaptiste Daroussin static void
mlinks_undupe(struct mpage * mpage)107761d06d6bSBaptiste Daroussin mlinks_undupe(struct mpage *mpage)
107861d06d6bSBaptiste Daroussin {
107961d06d6bSBaptiste Daroussin char buf[PATH_MAX];
108061d06d6bSBaptiste Daroussin struct mlink **prev;
108161d06d6bSBaptiste Daroussin struct mlink *mlink;
108261d06d6bSBaptiste Daroussin char *bufp;
108361d06d6bSBaptiste Daroussin
108461d06d6bSBaptiste Daroussin mpage->form = FORM_CAT;
108561d06d6bSBaptiste Daroussin prev = &mpage->mlinks;
108661d06d6bSBaptiste Daroussin while (NULL != (mlink = *prev)) {
108761d06d6bSBaptiste Daroussin if (FORM_CAT != mlink->dform) {
108861d06d6bSBaptiste Daroussin mpage->form = FORM_NONE;
108961d06d6bSBaptiste Daroussin goto nextlink;
109061d06d6bSBaptiste Daroussin }
109161d06d6bSBaptiste Daroussin (void)strlcpy(buf, mlink->file, sizeof(buf));
109261d06d6bSBaptiste Daroussin bufp = strstr(buf, "cat");
109361d06d6bSBaptiste Daroussin assert(NULL != bufp);
109461d06d6bSBaptiste Daroussin memcpy(bufp, "man", 3);
109561d06d6bSBaptiste Daroussin if (NULL != (bufp = strrchr(buf, '.')))
109661d06d6bSBaptiste Daroussin *++bufp = '\0';
109761d06d6bSBaptiste Daroussin (void)strlcat(buf, mlink->dsec, sizeof(buf));
109861d06d6bSBaptiste Daroussin if (NULL == ohash_find(&mlinks,
109961d06d6bSBaptiste Daroussin ohash_qlookup(&mlinks, buf)))
110061d06d6bSBaptiste Daroussin goto nextlink;
110161d06d6bSBaptiste Daroussin if (warnings)
110261d06d6bSBaptiste Daroussin say(mlink->file, "Man source exists: %s", buf);
110361d06d6bSBaptiste Daroussin if (use_all)
110461d06d6bSBaptiste Daroussin goto nextlink;
110561d06d6bSBaptiste Daroussin *prev = mlink->next;
110661d06d6bSBaptiste Daroussin mlink_free(mlink);
110761d06d6bSBaptiste Daroussin continue;
110861d06d6bSBaptiste Daroussin nextlink:
110961d06d6bSBaptiste Daroussin prev = &(*prev)->next;
111061d06d6bSBaptiste Daroussin }
111161d06d6bSBaptiste Daroussin }
111261d06d6bSBaptiste Daroussin
111361d06d6bSBaptiste Daroussin static void
mlink_check(struct mpage * mpage,struct mlink * mlink)111461d06d6bSBaptiste Daroussin mlink_check(struct mpage *mpage, struct mlink *mlink)
111561d06d6bSBaptiste Daroussin {
111661d06d6bSBaptiste Daroussin struct str *str;
111761d06d6bSBaptiste Daroussin unsigned int slot;
111861d06d6bSBaptiste Daroussin
111961d06d6bSBaptiste Daroussin /*
112061d06d6bSBaptiste Daroussin * Check whether the manual section given in a file
112161d06d6bSBaptiste Daroussin * agrees with the directory where the file is located.
112261d06d6bSBaptiste Daroussin * Some manuals have suffixes like (3p) on their
112361d06d6bSBaptiste Daroussin * section number either inside the file or in the
112461d06d6bSBaptiste Daroussin * directory name, some are linked into more than one
112561d06d6bSBaptiste Daroussin * section, like encrypt(1) = makekey(8).
112661d06d6bSBaptiste Daroussin */
112761d06d6bSBaptiste Daroussin
112861d06d6bSBaptiste Daroussin if (FORM_SRC == mpage->form &&
112961d06d6bSBaptiste Daroussin strcasecmp(mpage->sec, mlink->dsec))
113061d06d6bSBaptiste Daroussin say(mlink->file, "Section \"%s\" manual in %s directory",
113161d06d6bSBaptiste Daroussin mpage->sec, mlink->dsec);
113261d06d6bSBaptiste Daroussin
113361d06d6bSBaptiste Daroussin /*
113461d06d6bSBaptiste Daroussin * Manual page directories exist for each kernel
113561d06d6bSBaptiste Daroussin * architecture as returned by machine(1).
113661d06d6bSBaptiste Daroussin * However, many manuals only depend on the
113761d06d6bSBaptiste Daroussin * application architecture as returned by arch(1).
113861d06d6bSBaptiste Daroussin * For example, some (2/ARM) manuals are shared
113961d06d6bSBaptiste Daroussin * across the "armish" and "zaurus" kernel
114061d06d6bSBaptiste Daroussin * architectures.
114161d06d6bSBaptiste Daroussin * A few manuals are even shared across completely
114261d06d6bSBaptiste Daroussin * different architectures, for example fdformat(1)
114361d06d6bSBaptiste Daroussin * on amd64, i386, and sparc64.
114461d06d6bSBaptiste Daroussin */
114561d06d6bSBaptiste Daroussin
114661d06d6bSBaptiste Daroussin if (strcasecmp(mpage->arch, mlink->arch))
114761d06d6bSBaptiste Daroussin say(mlink->file, "Architecture \"%s\" manual in "
114861d06d6bSBaptiste Daroussin "\"%s\" directory", mpage->arch, mlink->arch);
114961d06d6bSBaptiste Daroussin
115061d06d6bSBaptiste Daroussin /*
115161d06d6bSBaptiste Daroussin * XXX
115261d06d6bSBaptiste Daroussin * parse_cat() doesn't set NAME_TITLE yet.
115361d06d6bSBaptiste Daroussin */
115461d06d6bSBaptiste Daroussin
115561d06d6bSBaptiste Daroussin if (FORM_CAT == mpage->form)
115661d06d6bSBaptiste Daroussin return;
115761d06d6bSBaptiste Daroussin
115861d06d6bSBaptiste Daroussin /*
115961d06d6bSBaptiste Daroussin * Check whether this mlink
116061d06d6bSBaptiste Daroussin * appears as a name in the NAME section.
116161d06d6bSBaptiste Daroussin */
116261d06d6bSBaptiste Daroussin
116361d06d6bSBaptiste Daroussin slot = ohash_qlookup(&names, mlink->name);
116461d06d6bSBaptiste Daroussin str = ohash_find(&names, slot);
116561d06d6bSBaptiste Daroussin assert(NULL != str);
116661d06d6bSBaptiste Daroussin if ( ! (NAME_TITLE & str->mask))
116761d06d6bSBaptiste Daroussin say(mlink->file, "Name missing in NAME section");
116861d06d6bSBaptiste Daroussin }
116961d06d6bSBaptiste Daroussin
117061d06d6bSBaptiste Daroussin /*
117161d06d6bSBaptiste Daroussin * Run through the files in the global vector "mpages"
117261d06d6bSBaptiste Daroussin * and add them to the database specified in "basedir".
117361d06d6bSBaptiste Daroussin *
117461d06d6bSBaptiste Daroussin * This handles the parsing scheme itself, using the cues of directory
117561d06d6bSBaptiste Daroussin * and filename to determine whether the file is parsable or not.
117661d06d6bSBaptiste Daroussin */
117761d06d6bSBaptiste Daroussin static void
mpages_merge(struct dba * dba,struct mparse * mp)117861d06d6bSBaptiste Daroussin mpages_merge(struct dba *dba, struct mparse *mp)
117961d06d6bSBaptiste Daroussin {
118061d06d6bSBaptiste Daroussin struct mpage *mpage, *mpage_dest;
118161d06d6bSBaptiste Daroussin struct mlink *mlink, *mlink_dest;
11827295610fSBaptiste Daroussin struct roff_meta *meta;
118361d06d6bSBaptiste Daroussin char *cp;
118461d06d6bSBaptiste Daroussin int fd;
118561d06d6bSBaptiste Daroussin
118661d06d6bSBaptiste Daroussin for (mpage = mpage_head; mpage != NULL; mpage = mpage->next) {
118761d06d6bSBaptiste Daroussin mlinks_undupe(mpage);
118861d06d6bSBaptiste Daroussin if ((mlink = mpage->mlinks) == NULL)
118961d06d6bSBaptiste Daroussin continue;
119061d06d6bSBaptiste Daroussin
119161d06d6bSBaptiste Daroussin name_mask = NAME_MASK;
119261d06d6bSBaptiste Daroussin mandoc_ohash_init(&names, 4, offsetof(struct str, key));
119361d06d6bSBaptiste Daroussin mandoc_ohash_init(&strings, 6, offsetof(struct str, key));
119461d06d6bSBaptiste Daroussin mparse_reset(mp);
11957295610fSBaptiste Daroussin meta = NULL;
119661d06d6bSBaptiste Daroussin
119761d06d6bSBaptiste Daroussin if ((fd = mparse_open(mp, mlink->file)) == -1) {
119861d06d6bSBaptiste Daroussin say(mlink->file, "&open");
119961d06d6bSBaptiste Daroussin goto nextpage;
120061d06d6bSBaptiste Daroussin }
120161d06d6bSBaptiste Daroussin
120261d06d6bSBaptiste Daroussin /*
120361d06d6bSBaptiste Daroussin * Interpret the file as mdoc(7) or man(7) source
120461d06d6bSBaptiste Daroussin * code, unless it is known to be formatted.
120561d06d6bSBaptiste Daroussin */
120661d06d6bSBaptiste Daroussin if (mlink->dform != FORM_CAT || mlink->fform != FORM_CAT) {
120761d06d6bSBaptiste Daroussin mparse_readfd(mp, fd, mlink->file);
120861d06d6bSBaptiste Daroussin close(fd);
120961d06d6bSBaptiste Daroussin fd = -1;
12107295610fSBaptiste Daroussin meta = mparse_result(mp);
121161d06d6bSBaptiste Daroussin }
121261d06d6bSBaptiste Daroussin
12137295610fSBaptiste Daroussin if (meta != NULL && meta->sodest != NULL) {
121461d06d6bSBaptiste Daroussin mlink_dest = ohash_find(&mlinks,
12157295610fSBaptiste Daroussin ohash_qlookup(&mlinks, meta->sodest));
121661d06d6bSBaptiste Daroussin if (mlink_dest == NULL) {
12177295610fSBaptiste Daroussin mandoc_asprintf(&cp, "%s.gz", meta->sodest);
121861d06d6bSBaptiste Daroussin mlink_dest = ohash_find(&mlinks,
121961d06d6bSBaptiste Daroussin ohash_qlookup(&mlinks, cp));
122061d06d6bSBaptiste Daroussin free(cp);
122161d06d6bSBaptiste Daroussin }
122261d06d6bSBaptiste Daroussin if (mlink_dest != NULL) {
122361d06d6bSBaptiste Daroussin
122461d06d6bSBaptiste Daroussin /* The .so target exists. */
122561d06d6bSBaptiste Daroussin
122661d06d6bSBaptiste Daroussin mpage_dest = mlink_dest->mpage;
122761d06d6bSBaptiste Daroussin while (1) {
122861d06d6bSBaptiste Daroussin mlink->mpage = mpage_dest;
122961d06d6bSBaptiste Daroussin
123061d06d6bSBaptiste Daroussin /*
123161d06d6bSBaptiste Daroussin * If the target was already
123261d06d6bSBaptiste Daroussin * processed, add the links
123361d06d6bSBaptiste Daroussin * to the database now.
123461d06d6bSBaptiste Daroussin * Otherwise, this will
123561d06d6bSBaptiste Daroussin * happen when we come
123661d06d6bSBaptiste Daroussin * to the target.
123761d06d6bSBaptiste Daroussin */
123861d06d6bSBaptiste Daroussin
123961d06d6bSBaptiste Daroussin if (mpage_dest->dba != NULL)
124061d06d6bSBaptiste Daroussin dbadd_mlink(mlink);
124161d06d6bSBaptiste Daroussin
124261d06d6bSBaptiste Daroussin if (mlink->next == NULL)
124361d06d6bSBaptiste Daroussin break;
124461d06d6bSBaptiste Daroussin mlink = mlink->next;
124561d06d6bSBaptiste Daroussin }
124661d06d6bSBaptiste Daroussin
124761d06d6bSBaptiste Daroussin /* Move all links to the target. */
124861d06d6bSBaptiste Daroussin
124961d06d6bSBaptiste Daroussin mlink->next = mlink_dest->next;
125061d06d6bSBaptiste Daroussin mlink_dest->next = mpage->mlinks;
125161d06d6bSBaptiste Daroussin mpage->mlinks = NULL;
125261d06d6bSBaptiste Daroussin goto nextpage;
125345a5aec3SBaptiste Daroussin }
125445a5aec3SBaptiste Daroussin meta->macroset = MACROSET_NONE;
125545a5aec3SBaptiste Daroussin }
125645a5aec3SBaptiste Daroussin if (meta != NULL && meta->macroset == MACROSET_MDOC) {
125761d06d6bSBaptiste Daroussin mpage->form = FORM_SRC;
12587295610fSBaptiste Daroussin mpage->sec = meta->msec;
125961d06d6bSBaptiste Daroussin mpage->sec = mandoc_strdup(
126061d06d6bSBaptiste Daroussin mpage->sec == NULL ? "" : mpage->sec);
12617295610fSBaptiste Daroussin mpage->arch = meta->arch;
126261d06d6bSBaptiste Daroussin mpage->arch = mandoc_strdup(
126361d06d6bSBaptiste Daroussin mpage->arch == NULL ? "" : mpage->arch);
12647295610fSBaptiste Daroussin mpage->title = mandoc_strdup(meta->title);
12657295610fSBaptiste Daroussin } else if (meta != NULL && meta->macroset == MACROSET_MAN) {
12667295610fSBaptiste Daroussin if (*meta->msec != '\0' || *meta->title != '\0') {
126761d06d6bSBaptiste Daroussin mpage->form = FORM_SRC;
12687295610fSBaptiste Daroussin mpage->sec = mandoc_strdup(meta->msec);
126961d06d6bSBaptiste Daroussin mpage->arch = mandoc_strdup(mlink->arch);
12707295610fSBaptiste Daroussin mpage->title = mandoc_strdup(meta->title);
127161d06d6bSBaptiste Daroussin } else
12727295610fSBaptiste Daroussin meta = NULL;
127361d06d6bSBaptiste Daroussin }
127461d06d6bSBaptiste Daroussin
127561d06d6bSBaptiste Daroussin assert(mpage->desc == NULL);
127645a5aec3SBaptiste Daroussin if (meta == NULL || meta->sodest != NULL) {
127761d06d6bSBaptiste Daroussin mpage->sec = mandoc_strdup(mlink->dsec);
127861d06d6bSBaptiste Daroussin mpage->arch = mandoc_strdup(mlink->arch);
127961d06d6bSBaptiste Daroussin mpage->title = mandoc_strdup(mlink->name);
128045a5aec3SBaptiste Daroussin if (meta == NULL) {
128145a5aec3SBaptiste Daroussin mpage->form = FORM_CAT;
128261d06d6bSBaptiste Daroussin parse_cat(mpage, fd);
128345a5aec3SBaptiste Daroussin } else
128445a5aec3SBaptiste Daroussin mpage->form = FORM_SRC;
12857295610fSBaptiste Daroussin } else if (meta->macroset == MACROSET_MDOC)
12867295610fSBaptiste Daroussin parse_mdoc(mpage, meta, meta->first);
128761d06d6bSBaptiste Daroussin else
12887295610fSBaptiste Daroussin parse_man(mpage, meta, meta->first);
128961d06d6bSBaptiste Daroussin if (mpage->desc == NULL) {
129061d06d6bSBaptiste Daroussin mpage->desc = mandoc_strdup(mlink->name);
129161d06d6bSBaptiste Daroussin if (warnings)
129261d06d6bSBaptiste Daroussin say(mlink->file, "No one-line description, "
129361d06d6bSBaptiste Daroussin "using filename \"%s\"", mlink->name);
129461d06d6bSBaptiste Daroussin }
129561d06d6bSBaptiste Daroussin
129661d06d6bSBaptiste Daroussin for (mlink = mpage->mlinks;
129761d06d6bSBaptiste Daroussin mlink != NULL;
129861d06d6bSBaptiste Daroussin mlink = mlink->next) {
129961d06d6bSBaptiste Daroussin putkey(mpage, mlink->name, NAME_FILE);
130061d06d6bSBaptiste Daroussin if (warnings && !use_all)
130161d06d6bSBaptiste Daroussin mlink_check(mpage, mlink);
130261d06d6bSBaptiste Daroussin }
130361d06d6bSBaptiste Daroussin
130461d06d6bSBaptiste Daroussin dbadd(dba, mpage);
130561d06d6bSBaptiste Daroussin
130661d06d6bSBaptiste Daroussin nextpage:
130761d06d6bSBaptiste Daroussin ohash_delete(&strings);
130861d06d6bSBaptiste Daroussin ohash_delete(&names);
130961d06d6bSBaptiste Daroussin }
131061d06d6bSBaptiste Daroussin }
131161d06d6bSBaptiste Daroussin
131261d06d6bSBaptiste Daroussin static void
parse_cat(struct mpage * mpage,int fd)131361d06d6bSBaptiste Daroussin parse_cat(struct mpage *mpage, int fd)
131461d06d6bSBaptiste Daroussin {
131561d06d6bSBaptiste Daroussin FILE *stream;
131661d06d6bSBaptiste Daroussin struct mlink *mlink;
131761d06d6bSBaptiste Daroussin char *line, *p, *title, *sec;
131861d06d6bSBaptiste Daroussin size_t linesz, plen, titlesz;
131961d06d6bSBaptiste Daroussin ssize_t len;
132061d06d6bSBaptiste Daroussin int offs;
132161d06d6bSBaptiste Daroussin
132261d06d6bSBaptiste Daroussin mlink = mpage->mlinks;
132361d06d6bSBaptiste Daroussin stream = fd == -1 ? fopen(mlink->file, "r") : fdopen(fd, "r");
132461d06d6bSBaptiste Daroussin if (stream == NULL) {
132561d06d6bSBaptiste Daroussin if (fd != -1)
132661d06d6bSBaptiste Daroussin close(fd);
132761d06d6bSBaptiste Daroussin if (warnings)
132861d06d6bSBaptiste Daroussin say(mlink->file, "&fopen");
132961d06d6bSBaptiste Daroussin return;
133061d06d6bSBaptiste Daroussin }
133161d06d6bSBaptiste Daroussin
133261d06d6bSBaptiste Daroussin line = NULL;
133361d06d6bSBaptiste Daroussin linesz = 0;
133461d06d6bSBaptiste Daroussin
133561d06d6bSBaptiste Daroussin /* Parse the section number from the header line. */
133661d06d6bSBaptiste Daroussin
133761d06d6bSBaptiste Daroussin while (getline(&line, &linesz, stream) != -1) {
133861d06d6bSBaptiste Daroussin if (*line == '\n')
133961d06d6bSBaptiste Daroussin continue;
134061d06d6bSBaptiste Daroussin if ((sec = strchr(line, '(')) == NULL)
134161d06d6bSBaptiste Daroussin break;
134261d06d6bSBaptiste Daroussin if ((p = strchr(++sec, ')')) == NULL)
134361d06d6bSBaptiste Daroussin break;
134461d06d6bSBaptiste Daroussin free(mpage->sec);
134561d06d6bSBaptiste Daroussin mpage->sec = mandoc_strndup(sec, p - sec);
134661d06d6bSBaptiste Daroussin if (warnings && *mlink->dsec != '\0' &&
134761d06d6bSBaptiste Daroussin strcasecmp(mpage->sec, mlink->dsec))
134861d06d6bSBaptiste Daroussin say(mlink->file,
134961d06d6bSBaptiste Daroussin "Section \"%s\" manual in %s directory",
135061d06d6bSBaptiste Daroussin mpage->sec, mlink->dsec);
135161d06d6bSBaptiste Daroussin break;
135261d06d6bSBaptiste Daroussin }
135361d06d6bSBaptiste Daroussin
135461d06d6bSBaptiste Daroussin /* Skip to first blank line. */
135561d06d6bSBaptiste Daroussin
135661d06d6bSBaptiste Daroussin while (line == NULL || *line != '\n')
135761d06d6bSBaptiste Daroussin if (getline(&line, &linesz, stream) == -1)
135861d06d6bSBaptiste Daroussin break;
135961d06d6bSBaptiste Daroussin
136061d06d6bSBaptiste Daroussin /*
136161d06d6bSBaptiste Daroussin * Assume the first line that is not indented
136261d06d6bSBaptiste Daroussin * is the first section header. Skip to it.
136361d06d6bSBaptiste Daroussin */
136461d06d6bSBaptiste Daroussin
136561d06d6bSBaptiste Daroussin while (getline(&line, &linesz, stream) != -1)
136661d06d6bSBaptiste Daroussin if (*line != '\n' && *line != ' ')
136761d06d6bSBaptiste Daroussin break;
136861d06d6bSBaptiste Daroussin
136961d06d6bSBaptiste Daroussin /*
137061d06d6bSBaptiste Daroussin * Read up until the next section into a buffer.
137161d06d6bSBaptiste Daroussin * Strip the leading and trailing newline from each read line,
137261d06d6bSBaptiste Daroussin * appending a trailing space.
137361d06d6bSBaptiste Daroussin * Ignore empty (whitespace-only) lines.
137461d06d6bSBaptiste Daroussin */
137561d06d6bSBaptiste Daroussin
137661d06d6bSBaptiste Daroussin titlesz = 0;
137761d06d6bSBaptiste Daroussin title = NULL;
137861d06d6bSBaptiste Daroussin
137961d06d6bSBaptiste Daroussin while ((len = getline(&line, &linesz, stream)) != -1) {
138061d06d6bSBaptiste Daroussin if (*line != ' ')
138161d06d6bSBaptiste Daroussin break;
138261d06d6bSBaptiste Daroussin offs = 0;
138361d06d6bSBaptiste Daroussin while (isspace((unsigned char)line[offs]))
138461d06d6bSBaptiste Daroussin offs++;
138561d06d6bSBaptiste Daroussin if (line[offs] == '\0')
138661d06d6bSBaptiste Daroussin continue;
138761d06d6bSBaptiste Daroussin title = mandoc_realloc(title, titlesz + len - offs);
138861d06d6bSBaptiste Daroussin memcpy(title + titlesz, line + offs, len - offs);
138961d06d6bSBaptiste Daroussin titlesz += len - offs;
139061d06d6bSBaptiste Daroussin title[titlesz - 1] = ' ';
139161d06d6bSBaptiste Daroussin }
139261d06d6bSBaptiste Daroussin free(line);
139361d06d6bSBaptiste Daroussin
139461d06d6bSBaptiste Daroussin /*
139561d06d6bSBaptiste Daroussin * If no page content can be found, or the input line
139661d06d6bSBaptiste Daroussin * is already the next section header, or there is no
139761d06d6bSBaptiste Daroussin * trailing newline, reuse the page title as the page
139861d06d6bSBaptiste Daroussin * description.
139961d06d6bSBaptiste Daroussin */
140061d06d6bSBaptiste Daroussin
140161d06d6bSBaptiste Daroussin if (NULL == title || '\0' == *title) {
140261d06d6bSBaptiste Daroussin if (warnings)
140361d06d6bSBaptiste Daroussin say(mlink->file, "Cannot find NAME section");
140461d06d6bSBaptiste Daroussin fclose(stream);
140561d06d6bSBaptiste Daroussin free(title);
140661d06d6bSBaptiste Daroussin return;
140761d06d6bSBaptiste Daroussin }
140861d06d6bSBaptiste Daroussin
140961d06d6bSBaptiste Daroussin title[titlesz - 1] = '\0';
141061d06d6bSBaptiste Daroussin
141161d06d6bSBaptiste Daroussin /*
141261d06d6bSBaptiste Daroussin * Skip to the first dash.
141361d06d6bSBaptiste Daroussin * Use the remaining line as the description (no more than 70
141461d06d6bSBaptiste Daroussin * bytes).
141561d06d6bSBaptiste Daroussin */
141661d06d6bSBaptiste Daroussin
141761d06d6bSBaptiste Daroussin if (NULL != (p = strstr(title, "- "))) {
141861d06d6bSBaptiste Daroussin for (p += 2; ' ' == *p || '\b' == *p; p++)
141961d06d6bSBaptiste Daroussin /* Skip to next word. */ ;
142061d06d6bSBaptiste Daroussin } else {
142161d06d6bSBaptiste Daroussin if (warnings)
142261d06d6bSBaptiste Daroussin say(mlink->file, "No dash in title line, "
142361d06d6bSBaptiste Daroussin "reusing \"%s\" as one-line description", title);
142461d06d6bSBaptiste Daroussin p = title;
142561d06d6bSBaptiste Daroussin }
142661d06d6bSBaptiste Daroussin
142761d06d6bSBaptiste Daroussin plen = strlen(p);
142861d06d6bSBaptiste Daroussin
142961d06d6bSBaptiste Daroussin /* Strip backspace-encoding from line. */
143061d06d6bSBaptiste Daroussin
143161d06d6bSBaptiste Daroussin while (NULL != (line = memchr(p, '\b', plen))) {
143261d06d6bSBaptiste Daroussin len = line - p;
143361d06d6bSBaptiste Daroussin if (0 == len) {
143461d06d6bSBaptiste Daroussin memmove(line, line + 1, plen--);
143561d06d6bSBaptiste Daroussin continue;
143661d06d6bSBaptiste Daroussin }
143761d06d6bSBaptiste Daroussin memmove(line - 1, line + 1, plen - len);
143861d06d6bSBaptiste Daroussin plen -= 2;
143961d06d6bSBaptiste Daroussin }
144061d06d6bSBaptiste Daroussin
144161d06d6bSBaptiste Daroussin /*
144261d06d6bSBaptiste Daroussin * Cut off excessive one-line descriptions.
144361d06d6bSBaptiste Daroussin * Bad pages are not worth better heuristics.
144461d06d6bSBaptiste Daroussin */
144561d06d6bSBaptiste Daroussin
144661d06d6bSBaptiste Daroussin mpage->desc = mandoc_strndup(p, 150);
144761d06d6bSBaptiste Daroussin fclose(stream);
144861d06d6bSBaptiste Daroussin free(title);
144961d06d6bSBaptiste Daroussin }
145061d06d6bSBaptiste Daroussin
145161d06d6bSBaptiste Daroussin /*
145261d06d6bSBaptiste Daroussin * Put a type/word pair into the word database for this particular file.
145361d06d6bSBaptiste Daroussin */
145461d06d6bSBaptiste Daroussin static void
putkey(const struct mpage * mpage,char * value,uint64_t type)145561d06d6bSBaptiste Daroussin putkey(const struct mpage *mpage, char *value, uint64_t type)
145661d06d6bSBaptiste Daroussin {
145761d06d6bSBaptiste Daroussin putkeys(mpage, value, strlen(value), type);
145861d06d6bSBaptiste Daroussin }
145961d06d6bSBaptiste Daroussin
146061d06d6bSBaptiste Daroussin /*
146161d06d6bSBaptiste Daroussin * Grok all nodes at or below a certain mdoc node into putkey().
146261d06d6bSBaptiste Daroussin */
146361d06d6bSBaptiste Daroussin static void
putmdockey(const struct mpage * mpage,const struct roff_node * n,uint64_t m,int taboo)146461d06d6bSBaptiste Daroussin putmdockey(const struct mpage *mpage,
146561d06d6bSBaptiste Daroussin const struct roff_node *n, uint64_t m, int taboo)
146661d06d6bSBaptiste Daroussin {
146761d06d6bSBaptiste Daroussin
146861d06d6bSBaptiste Daroussin for ( ; NULL != n; n = n->next) {
146961d06d6bSBaptiste Daroussin if (n->flags & taboo)
147061d06d6bSBaptiste Daroussin continue;
147161d06d6bSBaptiste Daroussin if (NULL != n->child)
147261d06d6bSBaptiste Daroussin putmdockey(mpage, n->child, m, taboo);
147361d06d6bSBaptiste Daroussin if (n->type == ROFFT_TEXT)
147461d06d6bSBaptiste Daroussin putkey(mpage, n->string, m);
147561d06d6bSBaptiste Daroussin }
147661d06d6bSBaptiste Daroussin }
147761d06d6bSBaptiste Daroussin
147861d06d6bSBaptiste Daroussin static void
parse_man(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)147961d06d6bSBaptiste Daroussin parse_man(struct mpage *mpage, const struct roff_meta *meta,
148061d06d6bSBaptiste Daroussin const struct roff_node *n)
148161d06d6bSBaptiste Daroussin {
148261d06d6bSBaptiste Daroussin const struct roff_node *head, *body;
148361d06d6bSBaptiste Daroussin char *start, *title;
148461d06d6bSBaptiste Daroussin char byte;
148561d06d6bSBaptiste Daroussin size_t sz;
148661d06d6bSBaptiste Daroussin
148761d06d6bSBaptiste Daroussin if (n == NULL)
148861d06d6bSBaptiste Daroussin return;
148961d06d6bSBaptiste Daroussin
149061d06d6bSBaptiste Daroussin /*
149161d06d6bSBaptiste Daroussin * We're only searching for one thing: the first text child in
149261d06d6bSBaptiste Daroussin * the BODY of a NAME section. Since we don't keep track of
149361d06d6bSBaptiste Daroussin * sections in -man, run some hoops to find out whether we're in
149461d06d6bSBaptiste Daroussin * the correct section or not.
149561d06d6bSBaptiste Daroussin */
149661d06d6bSBaptiste Daroussin
149761d06d6bSBaptiste Daroussin if (n->type == ROFFT_BODY && n->tok == MAN_SH) {
149861d06d6bSBaptiste Daroussin body = n;
149961d06d6bSBaptiste Daroussin if ((head = body->parent->head) != NULL &&
150061d06d6bSBaptiste Daroussin (head = head->child) != NULL &&
150161d06d6bSBaptiste Daroussin head->next == NULL &&
150261d06d6bSBaptiste Daroussin head->type == ROFFT_TEXT &&
150361d06d6bSBaptiste Daroussin strcmp(head->string, "NAME") == 0 &&
150461d06d6bSBaptiste Daroussin body->child != NULL) {
150561d06d6bSBaptiste Daroussin
150661d06d6bSBaptiste Daroussin /*
150761d06d6bSBaptiste Daroussin * Suck the entire NAME section into memory.
150861d06d6bSBaptiste Daroussin * Yes, we might run away.
150961d06d6bSBaptiste Daroussin * But too many manuals have big, spread-out
151061d06d6bSBaptiste Daroussin * NAME sections over many lines.
151161d06d6bSBaptiste Daroussin */
151261d06d6bSBaptiste Daroussin
151361d06d6bSBaptiste Daroussin title = NULL;
151461d06d6bSBaptiste Daroussin deroff(&title, body);
151561d06d6bSBaptiste Daroussin if (NULL == title)
151661d06d6bSBaptiste Daroussin return;
151761d06d6bSBaptiste Daroussin
151861d06d6bSBaptiste Daroussin /*
151961d06d6bSBaptiste Daroussin * Go through a special heuristic dance here.
152061d06d6bSBaptiste Daroussin * Conventionally, one or more manual names are
152161d06d6bSBaptiste Daroussin * comma-specified prior to a whitespace, then a
152261d06d6bSBaptiste Daroussin * dash, then a description. Try to puzzle out
152361d06d6bSBaptiste Daroussin * the name parts here.
152461d06d6bSBaptiste Daroussin */
152561d06d6bSBaptiste Daroussin
152661d06d6bSBaptiste Daroussin start = title;
152761d06d6bSBaptiste Daroussin for ( ;; ) {
152861d06d6bSBaptiste Daroussin sz = strcspn(start, " ,");
152961d06d6bSBaptiste Daroussin if ('\0' == start[sz])
153061d06d6bSBaptiste Daroussin break;
153161d06d6bSBaptiste Daroussin
153261d06d6bSBaptiste Daroussin byte = start[sz];
153361d06d6bSBaptiste Daroussin start[sz] = '\0';
153461d06d6bSBaptiste Daroussin
153561d06d6bSBaptiste Daroussin /*
153661d06d6bSBaptiste Daroussin * Assume a stray trailing comma in the
153761d06d6bSBaptiste Daroussin * name list if a name begins with a dash.
153861d06d6bSBaptiste Daroussin */
153961d06d6bSBaptiste Daroussin
154061d06d6bSBaptiste Daroussin if ('-' == start[0] ||
154161d06d6bSBaptiste Daroussin ('\\' == start[0] && '-' == start[1]))
154261d06d6bSBaptiste Daroussin break;
154361d06d6bSBaptiste Daroussin
154461d06d6bSBaptiste Daroussin putkey(mpage, start, NAME_TITLE);
154561d06d6bSBaptiste Daroussin if ( ! (mpage->name_head_done ||
154661d06d6bSBaptiste Daroussin strcasecmp(start, meta->title))) {
154761d06d6bSBaptiste Daroussin putkey(mpage, start, NAME_HEAD);
154861d06d6bSBaptiste Daroussin mpage->name_head_done = 1;
154961d06d6bSBaptiste Daroussin }
155061d06d6bSBaptiste Daroussin
155161d06d6bSBaptiste Daroussin if (' ' == byte) {
155261d06d6bSBaptiste Daroussin start += sz + 1;
155361d06d6bSBaptiste Daroussin break;
155461d06d6bSBaptiste Daroussin }
155561d06d6bSBaptiste Daroussin
155661d06d6bSBaptiste Daroussin assert(',' == byte);
155761d06d6bSBaptiste Daroussin start += sz + 1;
155861d06d6bSBaptiste Daroussin while (' ' == *start)
155961d06d6bSBaptiste Daroussin start++;
156061d06d6bSBaptiste Daroussin }
156161d06d6bSBaptiste Daroussin
156261d06d6bSBaptiste Daroussin if (start == title) {
156361d06d6bSBaptiste Daroussin putkey(mpage, start, NAME_TITLE);
156461d06d6bSBaptiste Daroussin if ( ! (mpage->name_head_done ||
156561d06d6bSBaptiste Daroussin strcasecmp(start, meta->title))) {
156661d06d6bSBaptiste Daroussin putkey(mpage, start, NAME_HEAD);
156761d06d6bSBaptiste Daroussin mpage->name_head_done = 1;
156861d06d6bSBaptiste Daroussin }
156961d06d6bSBaptiste Daroussin free(title);
157061d06d6bSBaptiste Daroussin return;
157161d06d6bSBaptiste Daroussin }
157261d06d6bSBaptiste Daroussin
157361d06d6bSBaptiste Daroussin while (isspace((unsigned char)*start))
157461d06d6bSBaptiste Daroussin start++;
157561d06d6bSBaptiste Daroussin
157661d06d6bSBaptiste Daroussin if (0 == strncmp(start, "-", 1))
157761d06d6bSBaptiste Daroussin start += 1;
157861d06d6bSBaptiste Daroussin else if (0 == strncmp(start, "\\-\\-", 4))
157961d06d6bSBaptiste Daroussin start += 4;
158061d06d6bSBaptiste Daroussin else if (0 == strncmp(start, "\\-", 2))
158161d06d6bSBaptiste Daroussin start += 2;
158261d06d6bSBaptiste Daroussin else if (0 == strncmp(start, "\\(en", 4))
158361d06d6bSBaptiste Daroussin start += 4;
158461d06d6bSBaptiste Daroussin else if (0 == strncmp(start, "\\(em", 4))
158561d06d6bSBaptiste Daroussin start += 4;
158661d06d6bSBaptiste Daroussin
158761d06d6bSBaptiste Daroussin while (' ' == *start)
158861d06d6bSBaptiste Daroussin start++;
158961d06d6bSBaptiste Daroussin
159061d06d6bSBaptiste Daroussin /*
159161d06d6bSBaptiste Daroussin * Cut off excessive one-line descriptions.
159261d06d6bSBaptiste Daroussin * Bad pages are not worth better heuristics.
159361d06d6bSBaptiste Daroussin */
159461d06d6bSBaptiste Daroussin
159561d06d6bSBaptiste Daroussin mpage->desc = mandoc_strndup(start, 150);
159661d06d6bSBaptiste Daroussin free(title);
159761d06d6bSBaptiste Daroussin return;
159861d06d6bSBaptiste Daroussin }
159961d06d6bSBaptiste Daroussin }
160061d06d6bSBaptiste Daroussin
160161d06d6bSBaptiste Daroussin for (n = n->child; n; n = n->next) {
160261d06d6bSBaptiste Daroussin if (NULL != mpage->desc)
160361d06d6bSBaptiste Daroussin break;
160461d06d6bSBaptiste Daroussin parse_man(mpage, meta, n);
160561d06d6bSBaptiste Daroussin }
160661d06d6bSBaptiste Daroussin }
160761d06d6bSBaptiste Daroussin
160861d06d6bSBaptiste Daroussin static void
parse_mdoc(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)160961d06d6bSBaptiste Daroussin parse_mdoc(struct mpage *mpage, const struct roff_meta *meta,
161061d06d6bSBaptiste Daroussin const struct roff_node *n)
161161d06d6bSBaptiste Daroussin {
16127295610fSBaptiste Daroussin const struct mdoc_handler *handler;
161361d06d6bSBaptiste Daroussin
161461d06d6bSBaptiste Daroussin for (n = n->child; n != NULL; n = n->next) {
16157295610fSBaptiste Daroussin if (n->tok == TOKEN_NONE || n->tok < ROFF_MAX)
161661d06d6bSBaptiste Daroussin continue;
161761d06d6bSBaptiste Daroussin assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
16187295610fSBaptiste Daroussin handler = mdoc_handlers + (n->tok - MDOC_Dd);
16197295610fSBaptiste Daroussin if (n->flags & handler->taboo)
16207295610fSBaptiste Daroussin continue;
16217295610fSBaptiste Daroussin
162261d06d6bSBaptiste Daroussin switch (n->type) {
162361d06d6bSBaptiste Daroussin case ROFFT_ELEM:
162461d06d6bSBaptiste Daroussin case ROFFT_BLOCK:
162561d06d6bSBaptiste Daroussin case ROFFT_HEAD:
162661d06d6bSBaptiste Daroussin case ROFFT_BODY:
162761d06d6bSBaptiste Daroussin case ROFFT_TAIL:
16287295610fSBaptiste Daroussin if (handler->fp != NULL &&
16297295610fSBaptiste Daroussin (*handler->fp)(mpage, meta, n) == 0)
163061d06d6bSBaptiste Daroussin break;
16317295610fSBaptiste Daroussin if (handler->mask)
163261d06d6bSBaptiste Daroussin putmdockey(mpage, n->child,
16337295610fSBaptiste Daroussin handler->mask, handler->taboo);
163461d06d6bSBaptiste Daroussin break;
163561d06d6bSBaptiste Daroussin default:
163661d06d6bSBaptiste Daroussin continue;
163761d06d6bSBaptiste Daroussin }
163861d06d6bSBaptiste Daroussin if (NULL != n->child)
163961d06d6bSBaptiste Daroussin parse_mdoc(mpage, meta, n);
164061d06d6bSBaptiste Daroussin }
164161d06d6bSBaptiste Daroussin }
164261d06d6bSBaptiste Daroussin
164361d06d6bSBaptiste Daroussin static int
parse_mdoc_Fa(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)164461d06d6bSBaptiste Daroussin parse_mdoc_Fa(struct mpage *mpage, const struct roff_meta *meta,
164561d06d6bSBaptiste Daroussin const struct roff_node *n)
164661d06d6bSBaptiste Daroussin {
164761d06d6bSBaptiste Daroussin uint64_t mask;
164861d06d6bSBaptiste Daroussin
164961d06d6bSBaptiste Daroussin mask = TYPE_Fa;
165061d06d6bSBaptiste Daroussin if (n->sec == SEC_SYNOPSIS)
165161d06d6bSBaptiste Daroussin mask |= TYPE_Vt;
165261d06d6bSBaptiste Daroussin
165361d06d6bSBaptiste Daroussin putmdockey(mpage, n->child, mask, 0);
165461d06d6bSBaptiste Daroussin return 0;
165561d06d6bSBaptiste Daroussin }
165661d06d6bSBaptiste Daroussin
165761d06d6bSBaptiste Daroussin static int
parse_mdoc_Fd(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)165861d06d6bSBaptiste Daroussin parse_mdoc_Fd(struct mpage *mpage, const struct roff_meta *meta,
165961d06d6bSBaptiste Daroussin const struct roff_node *n)
166061d06d6bSBaptiste Daroussin {
166161d06d6bSBaptiste Daroussin char *start, *end;
166261d06d6bSBaptiste Daroussin size_t sz;
166361d06d6bSBaptiste Daroussin
166461d06d6bSBaptiste Daroussin if (SEC_SYNOPSIS != n->sec ||
166561d06d6bSBaptiste Daroussin NULL == (n = n->child) ||
166661d06d6bSBaptiste Daroussin n->type != ROFFT_TEXT)
166761d06d6bSBaptiste Daroussin return 0;
166861d06d6bSBaptiste Daroussin
166961d06d6bSBaptiste Daroussin /*
167061d06d6bSBaptiste Daroussin * Only consider those `Fd' macro fields that begin with an
167161d06d6bSBaptiste Daroussin * "inclusion" token (versus, e.g., #define).
167261d06d6bSBaptiste Daroussin */
167361d06d6bSBaptiste Daroussin
167461d06d6bSBaptiste Daroussin if (strcmp("#include", n->string))
167561d06d6bSBaptiste Daroussin return 0;
167661d06d6bSBaptiste Daroussin
167761d06d6bSBaptiste Daroussin if ((n = n->next) == NULL || n->type != ROFFT_TEXT)
167861d06d6bSBaptiste Daroussin return 0;
167961d06d6bSBaptiste Daroussin
168061d06d6bSBaptiste Daroussin /*
168161d06d6bSBaptiste Daroussin * Strip away the enclosing angle brackets and make sure we're
168261d06d6bSBaptiste Daroussin * not zero-length.
168361d06d6bSBaptiste Daroussin */
168461d06d6bSBaptiste Daroussin
168561d06d6bSBaptiste Daroussin start = n->string;
168661d06d6bSBaptiste Daroussin if ('<' == *start || '"' == *start)
168761d06d6bSBaptiste Daroussin start++;
168861d06d6bSBaptiste Daroussin
168961d06d6bSBaptiste Daroussin if (0 == (sz = strlen(start)))
169061d06d6bSBaptiste Daroussin return 0;
169161d06d6bSBaptiste Daroussin
169261d06d6bSBaptiste Daroussin end = &start[(int)sz - 1];
169361d06d6bSBaptiste Daroussin if ('>' == *end || '"' == *end)
169461d06d6bSBaptiste Daroussin end--;
169561d06d6bSBaptiste Daroussin
169661d06d6bSBaptiste Daroussin if (end > start)
169761d06d6bSBaptiste Daroussin putkeys(mpage, start, end - start + 1, TYPE_In);
169861d06d6bSBaptiste Daroussin return 0;
169961d06d6bSBaptiste Daroussin }
170061d06d6bSBaptiste Daroussin
170161d06d6bSBaptiste Daroussin static void
parse_mdoc_fname(struct mpage * mpage,const struct roff_node * n)170261d06d6bSBaptiste Daroussin parse_mdoc_fname(struct mpage *mpage, const struct roff_node *n)
170361d06d6bSBaptiste Daroussin {
170461d06d6bSBaptiste Daroussin char *cp;
170561d06d6bSBaptiste Daroussin size_t sz;
170661d06d6bSBaptiste Daroussin
170761d06d6bSBaptiste Daroussin if (n->type != ROFFT_TEXT)
170861d06d6bSBaptiste Daroussin return;
170961d06d6bSBaptiste Daroussin
171061d06d6bSBaptiste Daroussin /* Skip function pointer punctuation. */
171161d06d6bSBaptiste Daroussin
171261d06d6bSBaptiste Daroussin cp = n->string;
171361d06d6bSBaptiste Daroussin while (*cp == '(' || *cp == '*')
171461d06d6bSBaptiste Daroussin cp++;
171561d06d6bSBaptiste Daroussin sz = strcspn(cp, "()");
171661d06d6bSBaptiste Daroussin
171761d06d6bSBaptiste Daroussin putkeys(mpage, cp, sz, TYPE_Fn);
171861d06d6bSBaptiste Daroussin if (n->sec == SEC_SYNOPSIS)
171961d06d6bSBaptiste Daroussin putkeys(mpage, cp, sz, NAME_SYN);
172061d06d6bSBaptiste Daroussin }
172161d06d6bSBaptiste Daroussin
172261d06d6bSBaptiste Daroussin static int
parse_mdoc_Fn(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)172361d06d6bSBaptiste Daroussin parse_mdoc_Fn(struct mpage *mpage, const struct roff_meta *meta,
172461d06d6bSBaptiste Daroussin const struct roff_node *n)
172561d06d6bSBaptiste Daroussin {
172661d06d6bSBaptiste Daroussin uint64_t mask;
172761d06d6bSBaptiste Daroussin
172861d06d6bSBaptiste Daroussin if (n->child == NULL)
172961d06d6bSBaptiste Daroussin return 0;
173061d06d6bSBaptiste Daroussin
173161d06d6bSBaptiste Daroussin parse_mdoc_fname(mpage, n->child);
173261d06d6bSBaptiste Daroussin
173361d06d6bSBaptiste Daroussin n = n->child->next;
173461d06d6bSBaptiste Daroussin if (n != NULL && n->type == ROFFT_TEXT) {
173561d06d6bSBaptiste Daroussin mask = TYPE_Fa;
173661d06d6bSBaptiste Daroussin if (n->sec == SEC_SYNOPSIS)
173761d06d6bSBaptiste Daroussin mask |= TYPE_Vt;
173861d06d6bSBaptiste Daroussin putmdockey(mpage, n, mask, 0);
173961d06d6bSBaptiste Daroussin }
174061d06d6bSBaptiste Daroussin
174161d06d6bSBaptiste Daroussin return 0;
174261d06d6bSBaptiste Daroussin }
174361d06d6bSBaptiste Daroussin
174461d06d6bSBaptiste Daroussin static int
parse_mdoc_Fo(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)174561d06d6bSBaptiste Daroussin parse_mdoc_Fo(struct mpage *mpage, const struct roff_meta *meta,
174661d06d6bSBaptiste Daroussin const struct roff_node *n)
174761d06d6bSBaptiste Daroussin {
174861d06d6bSBaptiste Daroussin
174961d06d6bSBaptiste Daroussin if (n->type != ROFFT_HEAD)
175061d06d6bSBaptiste Daroussin return 1;
175161d06d6bSBaptiste Daroussin
175261d06d6bSBaptiste Daroussin if (n->child != NULL)
175361d06d6bSBaptiste Daroussin parse_mdoc_fname(mpage, n->child);
175461d06d6bSBaptiste Daroussin
175561d06d6bSBaptiste Daroussin return 0;
175661d06d6bSBaptiste Daroussin }
175761d06d6bSBaptiste Daroussin
175861d06d6bSBaptiste Daroussin static int
parse_mdoc_Lb(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)1759*4c07abdbSAlexander Ziaee parse_mdoc_Lb(struct mpage *mpage, const struct roff_meta *meta,
1760*4c07abdbSAlexander Ziaee const struct roff_node *n)
1761*4c07abdbSAlexander Ziaee {
1762*4c07abdbSAlexander Ziaee char *cp;
1763*4c07abdbSAlexander Ziaee
1764*4c07abdbSAlexander Ziaee for (n = n->child; n != NULL; n = n->next) {
1765*4c07abdbSAlexander Ziaee if (n->flags & NODE_NOSRC)
1766*4c07abdbSAlexander Ziaee continue;
1767*4c07abdbSAlexander Ziaee cp = n->string;
1768*4c07abdbSAlexander Ziaee if (n->sec == SEC_SYNOPSIS)
1769*4c07abdbSAlexander Ziaee mandoc_asprintf(&cp, "lib%s", cp);
1770*4c07abdbSAlexander Ziaee putkey(mpage, cp, TYPE_Lb);
1771*4c07abdbSAlexander Ziaee if (n->sec == SEC_SYNOPSIS)
1772*4c07abdbSAlexander Ziaee free(cp);
1773*4c07abdbSAlexander Ziaee }
1774*4c07abdbSAlexander Ziaee return 0;
1775*4c07abdbSAlexander Ziaee }
1776*4c07abdbSAlexander Ziaee
1777*4c07abdbSAlexander Ziaee static int
parse_mdoc_Va(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)177861d06d6bSBaptiste Daroussin parse_mdoc_Va(struct mpage *mpage, const struct roff_meta *meta,
177961d06d6bSBaptiste Daroussin const struct roff_node *n)
178061d06d6bSBaptiste Daroussin {
178161d06d6bSBaptiste Daroussin char *cp;
178261d06d6bSBaptiste Daroussin
178361d06d6bSBaptiste Daroussin if (n->type != ROFFT_ELEM && n->type != ROFFT_BODY)
178461d06d6bSBaptiste Daroussin return 0;
178561d06d6bSBaptiste Daroussin
178661d06d6bSBaptiste Daroussin if (n->child != NULL &&
178761d06d6bSBaptiste Daroussin n->child->next == NULL &&
178861d06d6bSBaptiste Daroussin n->child->type == ROFFT_TEXT)
178961d06d6bSBaptiste Daroussin return 1;
179061d06d6bSBaptiste Daroussin
179161d06d6bSBaptiste Daroussin cp = NULL;
179261d06d6bSBaptiste Daroussin deroff(&cp, n);
179361d06d6bSBaptiste Daroussin if (cp != NULL) {
179461d06d6bSBaptiste Daroussin putkey(mpage, cp, TYPE_Vt | (n->tok == MDOC_Va ||
179561d06d6bSBaptiste Daroussin n->type == ROFFT_BODY ? TYPE_Va : 0));
179661d06d6bSBaptiste Daroussin free(cp);
179761d06d6bSBaptiste Daroussin }
179861d06d6bSBaptiste Daroussin
179961d06d6bSBaptiste Daroussin return 0;
180061d06d6bSBaptiste Daroussin }
180161d06d6bSBaptiste Daroussin
180261d06d6bSBaptiste Daroussin static int
parse_mdoc_Xr(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)180361d06d6bSBaptiste Daroussin parse_mdoc_Xr(struct mpage *mpage, const struct roff_meta *meta,
180461d06d6bSBaptiste Daroussin const struct roff_node *n)
180561d06d6bSBaptiste Daroussin {
180661d06d6bSBaptiste Daroussin char *cp;
180761d06d6bSBaptiste Daroussin
180861d06d6bSBaptiste Daroussin if (NULL == (n = n->child))
180961d06d6bSBaptiste Daroussin return 0;
181061d06d6bSBaptiste Daroussin
181161d06d6bSBaptiste Daroussin if (NULL == n->next) {
181261d06d6bSBaptiste Daroussin putkey(mpage, n->string, TYPE_Xr);
181361d06d6bSBaptiste Daroussin return 0;
181461d06d6bSBaptiste Daroussin }
181561d06d6bSBaptiste Daroussin
181661d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "%s(%s)", n->string, n->next->string);
181761d06d6bSBaptiste Daroussin putkey(mpage, cp, TYPE_Xr);
181861d06d6bSBaptiste Daroussin free(cp);
181961d06d6bSBaptiste Daroussin return 0;
182061d06d6bSBaptiste Daroussin }
182161d06d6bSBaptiste Daroussin
182261d06d6bSBaptiste Daroussin static int
parse_mdoc_Nd(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)182361d06d6bSBaptiste Daroussin parse_mdoc_Nd(struct mpage *mpage, const struct roff_meta *meta,
182461d06d6bSBaptiste Daroussin const struct roff_node *n)
182561d06d6bSBaptiste Daroussin {
182661d06d6bSBaptiste Daroussin
182761d06d6bSBaptiste Daroussin if (n->type == ROFFT_BODY)
182861d06d6bSBaptiste Daroussin deroff(&mpage->desc, n);
182961d06d6bSBaptiste Daroussin return 0;
183061d06d6bSBaptiste Daroussin }
183161d06d6bSBaptiste Daroussin
183261d06d6bSBaptiste Daroussin static int
parse_mdoc_Nm(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)183361d06d6bSBaptiste Daroussin parse_mdoc_Nm(struct mpage *mpage, const struct roff_meta *meta,
183461d06d6bSBaptiste Daroussin const struct roff_node *n)
183561d06d6bSBaptiste Daroussin {
183661d06d6bSBaptiste Daroussin
183761d06d6bSBaptiste Daroussin if (SEC_NAME == n->sec)
183861d06d6bSBaptiste Daroussin putmdockey(mpage, n->child, NAME_TITLE, 0);
183961d06d6bSBaptiste Daroussin else if (n->sec == SEC_SYNOPSIS && n->type == ROFFT_HEAD) {
184061d06d6bSBaptiste Daroussin if (n->child == NULL)
184161d06d6bSBaptiste Daroussin putkey(mpage, meta->name, NAME_SYN);
184261d06d6bSBaptiste Daroussin else
184361d06d6bSBaptiste Daroussin putmdockey(mpage, n->child, NAME_SYN, 0);
184461d06d6bSBaptiste Daroussin }
184561d06d6bSBaptiste Daroussin if ( ! (mpage->name_head_done ||
184661d06d6bSBaptiste Daroussin n->child == NULL || n->child->string == NULL ||
184761d06d6bSBaptiste Daroussin strcasecmp(n->child->string, meta->title))) {
184861d06d6bSBaptiste Daroussin putkey(mpage, n->child->string, NAME_HEAD);
184961d06d6bSBaptiste Daroussin mpage->name_head_done = 1;
185061d06d6bSBaptiste Daroussin }
185161d06d6bSBaptiste Daroussin return 0;
185261d06d6bSBaptiste Daroussin }
185361d06d6bSBaptiste Daroussin
185461d06d6bSBaptiste Daroussin static int
parse_mdoc_Sh(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)185561d06d6bSBaptiste Daroussin parse_mdoc_Sh(struct mpage *mpage, const struct roff_meta *meta,
185661d06d6bSBaptiste Daroussin const struct roff_node *n)
185761d06d6bSBaptiste Daroussin {
185861d06d6bSBaptiste Daroussin
185961d06d6bSBaptiste Daroussin return n->sec == SEC_CUSTOM && n->type == ROFFT_HEAD;
186061d06d6bSBaptiste Daroussin }
186161d06d6bSBaptiste Daroussin
186261d06d6bSBaptiste Daroussin static int
parse_mdoc_head(struct mpage * mpage,const struct roff_meta * meta,const struct roff_node * n)186361d06d6bSBaptiste Daroussin parse_mdoc_head(struct mpage *mpage, const struct roff_meta *meta,
186461d06d6bSBaptiste Daroussin const struct roff_node *n)
186561d06d6bSBaptiste Daroussin {
186661d06d6bSBaptiste Daroussin
186761d06d6bSBaptiste Daroussin return n->type == ROFFT_HEAD;
186861d06d6bSBaptiste Daroussin }
186961d06d6bSBaptiste Daroussin
187061d06d6bSBaptiste Daroussin /*
187161d06d6bSBaptiste Daroussin * Add a string to the hash table for the current manual.
187261d06d6bSBaptiste Daroussin * Each string has a bitmask telling which macros it belongs to.
187361d06d6bSBaptiste Daroussin * When we finish the manual, we'll dump the table.
187461d06d6bSBaptiste Daroussin */
187561d06d6bSBaptiste Daroussin static void
putkeys(const struct mpage * mpage,char * cp,size_t sz,uint64_t v)187661d06d6bSBaptiste Daroussin putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v)
187761d06d6bSBaptiste Daroussin {
187861d06d6bSBaptiste Daroussin struct ohash *htab;
187961d06d6bSBaptiste Daroussin struct str *s;
188061d06d6bSBaptiste Daroussin const char *end;
188161d06d6bSBaptiste Daroussin unsigned int slot;
188261d06d6bSBaptiste Daroussin int i, mustfree;
188361d06d6bSBaptiste Daroussin
188461d06d6bSBaptiste Daroussin if (0 == sz)
188561d06d6bSBaptiste Daroussin return;
188661d06d6bSBaptiste Daroussin
188761d06d6bSBaptiste Daroussin mustfree = render_string(&cp, &sz);
188861d06d6bSBaptiste Daroussin
188961d06d6bSBaptiste Daroussin if (TYPE_Nm & v) {
189061d06d6bSBaptiste Daroussin htab = &names;
189161d06d6bSBaptiste Daroussin v &= name_mask;
189261d06d6bSBaptiste Daroussin if (v & NAME_FIRST)
189361d06d6bSBaptiste Daroussin name_mask &= ~NAME_FIRST;
189461d06d6bSBaptiste Daroussin if (debug > 1)
189561d06d6bSBaptiste Daroussin say(mpage->mlinks->file,
189661d06d6bSBaptiste Daroussin "Adding name %*s, bits=0x%llx", (int)sz, cp,
189761d06d6bSBaptiste Daroussin (unsigned long long)v);
189861d06d6bSBaptiste Daroussin } else {
189961d06d6bSBaptiste Daroussin htab = &strings;
190061d06d6bSBaptiste Daroussin if (debug > 1)
190161d06d6bSBaptiste Daroussin for (i = 0; i < KEY_MAX; i++)
190261d06d6bSBaptiste Daroussin if ((uint64_t)1 << i & v)
190361d06d6bSBaptiste Daroussin say(mpage->mlinks->file,
190461d06d6bSBaptiste Daroussin "Adding key %s=%*s",
190561d06d6bSBaptiste Daroussin mansearch_keynames[i], (int)sz, cp);
190661d06d6bSBaptiste Daroussin }
190761d06d6bSBaptiste Daroussin
190861d06d6bSBaptiste Daroussin end = cp + sz;
190961d06d6bSBaptiste Daroussin slot = ohash_qlookupi(htab, cp, &end);
191061d06d6bSBaptiste Daroussin s = ohash_find(htab, slot);
191161d06d6bSBaptiste Daroussin
191261d06d6bSBaptiste Daroussin if (NULL != s && mpage == s->mpage) {
191361d06d6bSBaptiste Daroussin s->mask |= v;
191461d06d6bSBaptiste Daroussin return;
191561d06d6bSBaptiste Daroussin } else if (NULL == s) {
191661d06d6bSBaptiste Daroussin s = mandoc_calloc(1, sizeof(struct str) + sz + 1);
191761d06d6bSBaptiste Daroussin memcpy(s->key, cp, sz);
191861d06d6bSBaptiste Daroussin ohash_insert(htab, slot, s);
191961d06d6bSBaptiste Daroussin }
192061d06d6bSBaptiste Daroussin s->mpage = mpage;
192161d06d6bSBaptiste Daroussin s->mask = v;
192261d06d6bSBaptiste Daroussin
192361d06d6bSBaptiste Daroussin if (mustfree)
192461d06d6bSBaptiste Daroussin free(cp);
192561d06d6bSBaptiste Daroussin }
192661d06d6bSBaptiste Daroussin
192761d06d6bSBaptiste Daroussin /*
192861d06d6bSBaptiste Daroussin * Take a Unicode codepoint and produce its UTF-8 encoding.
192961d06d6bSBaptiste Daroussin * This isn't the best way to do this, but it works.
193061d06d6bSBaptiste Daroussin * The magic numbers are from the UTF-8 packaging.
1931c1c95addSBrooks Davis * Read the UTF-8 spec or the utf8(7) manual page for details.
193261d06d6bSBaptiste Daroussin */
193361d06d6bSBaptiste Daroussin static size_t
utf8(unsigned int cp,char out[5])1934c1c95addSBrooks Davis utf8(unsigned int cp, char out[5])
193561d06d6bSBaptiste Daroussin {
193661d06d6bSBaptiste Daroussin size_t rc;
193761d06d6bSBaptiste Daroussin
1938c1c95addSBrooks Davis if (cp <= 0x7f) {
193961d06d6bSBaptiste Daroussin rc = 1;
194061d06d6bSBaptiste Daroussin out[0] = (char)cp;
1941c1c95addSBrooks Davis } else if (cp <= 0x7ff) {
194261d06d6bSBaptiste Daroussin rc = 2;
194361d06d6bSBaptiste Daroussin out[0] = (cp >> 6 & 31) | 192;
194461d06d6bSBaptiste Daroussin out[1] = (cp & 63) | 128;
1945c1c95addSBrooks Davis } else if (cp >= 0xd800 && cp <= 0xdfff) {
1946c1c95addSBrooks Davis rc = 0; /* reject UTF-16 surrogate */
1947c1c95addSBrooks Davis } else if (cp <= 0xffff) {
194861d06d6bSBaptiste Daroussin rc = 3;
194961d06d6bSBaptiste Daroussin out[0] = (cp >> 12 & 15) | 224;
195061d06d6bSBaptiste Daroussin out[1] = (cp >> 6 & 63) | 128;
195161d06d6bSBaptiste Daroussin out[2] = (cp & 63) | 128;
1952c1c95addSBrooks Davis } else if (cp <= 0x10ffff) {
195361d06d6bSBaptiste Daroussin rc = 4;
195461d06d6bSBaptiste Daroussin out[0] = (cp >> 18 & 7) | 240;
195561d06d6bSBaptiste Daroussin out[1] = (cp >> 12 & 63) | 128;
195661d06d6bSBaptiste Daroussin out[2] = (cp >> 6 & 63) | 128;
195761d06d6bSBaptiste Daroussin out[3] = (cp & 63) | 128;
195861d06d6bSBaptiste Daroussin } else
1959c1c95addSBrooks Davis rc = 0;
196061d06d6bSBaptiste Daroussin
196161d06d6bSBaptiste Daroussin out[rc] = '\0';
196261d06d6bSBaptiste Daroussin return rc;
196361d06d6bSBaptiste Daroussin }
196461d06d6bSBaptiste Daroussin
196561d06d6bSBaptiste Daroussin /*
196661d06d6bSBaptiste Daroussin * If the string contains escape sequences,
196761d06d6bSBaptiste Daroussin * replace it with an allocated rendering and return 1,
196861d06d6bSBaptiste Daroussin * such that the caller can free it after use.
196961d06d6bSBaptiste Daroussin * Otherwise, do nothing and return 0.
197061d06d6bSBaptiste Daroussin */
197161d06d6bSBaptiste Daroussin static int
render_string(char ** public,size_t * psz)197261d06d6bSBaptiste Daroussin render_string(char **public, size_t *psz)
197361d06d6bSBaptiste Daroussin {
197461d06d6bSBaptiste Daroussin const char *src, *scp, *addcp, *seq;
197561d06d6bSBaptiste Daroussin char *dst;
197661d06d6bSBaptiste Daroussin size_t ssz, dsz, addsz;
197761d06d6bSBaptiste Daroussin char utfbuf[7], res[6];
197861d06d6bSBaptiste Daroussin int seqlen, unicode;
197961d06d6bSBaptiste Daroussin
198061d06d6bSBaptiste Daroussin res[0] = '\\';
198161d06d6bSBaptiste Daroussin res[1] = '\t';
198261d06d6bSBaptiste Daroussin res[2] = ASCII_NBRSP;
198361d06d6bSBaptiste Daroussin res[3] = ASCII_HYPH;
198461d06d6bSBaptiste Daroussin res[4] = ASCII_BREAK;
198561d06d6bSBaptiste Daroussin res[5] = '\0';
198661d06d6bSBaptiste Daroussin
198761d06d6bSBaptiste Daroussin src = scp = *public;
198861d06d6bSBaptiste Daroussin ssz = *psz;
198961d06d6bSBaptiste Daroussin dst = NULL;
199061d06d6bSBaptiste Daroussin dsz = 0;
199161d06d6bSBaptiste Daroussin
199261d06d6bSBaptiste Daroussin while (scp < src + *psz) {
199361d06d6bSBaptiste Daroussin
199461d06d6bSBaptiste Daroussin /* Leave normal characters unchanged. */
199561d06d6bSBaptiste Daroussin
199661d06d6bSBaptiste Daroussin if (strchr(res, *scp) == NULL) {
199761d06d6bSBaptiste Daroussin if (dst != NULL)
199861d06d6bSBaptiste Daroussin dst[dsz++] = *scp;
199961d06d6bSBaptiste Daroussin scp++;
200061d06d6bSBaptiste Daroussin continue;
200161d06d6bSBaptiste Daroussin }
200261d06d6bSBaptiste Daroussin
200361d06d6bSBaptiste Daroussin /*
200461d06d6bSBaptiste Daroussin * Found something that requires replacing,
200561d06d6bSBaptiste Daroussin * make sure we have a destination buffer.
200661d06d6bSBaptiste Daroussin */
200761d06d6bSBaptiste Daroussin
200861d06d6bSBaptiste Daroussin if (dst == NULL) {
200961d06d6bSBaptiste Daroussin dst = mandoc_malloc(ssz + 1);
201061d06d6bSBaptiste Daroussin dsz = scp - src;
201161d06d6bSBaptiste Daroussin memcpy(dst, src, dsz);
201261d06d6bSBaptiste Daroussin }
201361d06d6bSBaptiste Daroussin
201461d06d6bSBaptiste Daroussin /* Handle single-char special characters. */
201561d06d6bSBaptiste Daroussin
201661d06d6bSBaptiste Daroussin switch (*scp) {
201761d06d6bSBaptiste Daroussin case '\\':
201861d06d6bSBaptiste Daroussin break;
201961d06d6bSBaptiste Daroussin case '\t':
202061d06d6bSBaptiste Daroussin case ASCII_NBRSP:
202161d06d6bSBaptiste Daroussin dst[dsz++] = ' ';
202261d06d6bSBaptiste Daroussin scp++;
202361d06d6bSBaptiste Daroussin continue;
202461d06d6bSBaptiste Daroussin case ASCII_HYPH:
202561d06d6bSBaptiste Daroussin dst[dsz++] = '-';
202661d06d6bSBaptiste Daroussin /* FALLTHROUGH */
202761d06d6bSBaptiste Daroussin case ASCII_BREAK:
202861d06d6bSBaptiste Daroussin scp++;
202961d06d6bSBaptiste Daroussin continue;
203061d06d6bSBaptiste Daroussin default:
203161d06d6bSBaptiste Daroussin abort();
203261d06d6bSBaptiste Daroussin }
203361d06d6bSBaptiste Daroussin
203461d06d6bSBaptiste Daroussin /*
203561d06d6bSBaptiste Daroussin * Found an escape sequence.
203661d06d6bSBaptiste Daroussin * Read past the slash, then parse it.
203761d06d6bSBaptiste Daroussin * Ignore everything except characters.
203861d06d6bSBaptiste Daroussin */
203961d06d6bSBaptiste Daroussin
204061d06d6bSBaptiste Daroussin scp++;
2041c1c95addSBrooks Davis switch (mandoc_escape(&scp, &seq, &seqlen)) {
2042c1c95addSBrooks Davis case ESCAPE_UNICODE:
2043c1c95addSBrooks Davis unicode = mchars_num2uc(seq + 1, seqlen - 1);
2044c1c95addSBrooks Davis break;
2045c1c95addSBrooks Davis case ESCAPE_NUMBERED:
2046c1c95addSBrooks Davis unicode = mchars_num2char(seq, seqlen);
2047c1c95addSBrooks Davis break;
2048c1c95addSBrooks Davis case ESCAPE_SPECIAL:
2049c1c95addSBrooks Davis unicode = mchars_spec2cp(seq, seqlen);
2050c1c95addSBrooks Davis break;
2051c1c95addSBrooks Davis default:
2052c1c95addSBrooks Davis unicode = -1;
2053c1c95addSBrooks Davis break;
2054c1c95addSBrooks Davis }
2055c1c95addSBrooks Davis if (unicode <= 0)
205661d06d6bSBaptiste Daroussin continue;
205761d06d6bSBaptiste Daroussin
205861d06d6bSBaptiste Daroussin /*
205961d06d6bSBaptiste Daroussin * Render the special character
206061d06d6bSBaptiste Daroussin * as either UTF-8 or ASCII.
206161d06d6bSBaptiste Daroussin */
206261d06d6bSBaptiste Daroussin
206361d06d6bSBaptiste Daroussin if (write_utf8) {
206461d06d6bSBaptiste Daroussin addsz = utf8(unicode, utfbuf);
206561d06d6bSBaptiste Daroussin if (addsz == 0)
206661d06d6bSBaptiste Daroussin continue;
206761d06d6bSBaptiste Daroussin addcp = utfbuf;
206861d06d6bSBaptiste Daroussin } else {
2069c1c95addSBrooks Davis addcp = mchars_uc2str(unicode);
207061d06d6bSBaptiste Daroussin if (addcp == NULL)
207161d06d6bSBaptiste Daroussin continue;
2072c1c95addSBrooks Davis if (*addcp == ASCII_NBRSP)
207361d06d6bSBaptiste Daroussin addcp = " ";
2074c1c95addSBrooks Davis addsz = strlen(addcp);
207561d06d6bSBaptiste Daroussin }
207661d06d6bSBaptiste Daroussin
207761d06d6bSBaptiste Daroussin /* Copy the rendered glyph into the stream. */
207861d06d6bSBaptiste Daroussin
207961d06d6bSBaptiste Daroussin ssz += addsz;
208061d06d6bSBaptiste Daroussin dst = mandoc_realloc(dst, ssz + 1);
208161d06d6bSBaptiste Daroussin memcpy(dst + dsz, addcp, addsz);
208261d06d6bSBaptiste Daroussin dsz += addsz;
208361d06d6bSBaptiste Daroussin }
208461d06d6bSBaptiste Daroussin if (dst != NULL) {
208561d06d6bSBaptiste Daroussin *public = dst;
208661d06d6bSBaptiste Daroussin *psz = dsz;
208761d06d6bSBaptiste Daroussin }
208861d06d6bSBaptiste Daroussin
208961d06d6bSBaptiste Daroussin /* Trim trailing whitespace and NUL-terminate. */
209061d06d6bSBaptiste Daroussin
209161d06d6bSBaptiste Daroussin while (*psz > 0 && (*public)[*psz - 1] == ' ')
209261d06d6bSBaptiste Daroussin --*psz;
209361d06d6bSBaptiste Daroussin if (dst != NULL) {
209461d06d6bSBaptiste Daroussin (*public)[*psz] = '\0';
209561d06d6bSBaptiste Daroussin return 1;
209661d06d6bSBaptiste Daroussin } else
209761d06d6bSBaptiste Daroussin return 0;
209861d06d6bSBaptiste Daroussin }
209961d06d6bSBaptiste Daroussin
210061d06d6bSBaptiste Daroussin static void
dbadd_mlink(const struct mlink * mlink)210161d06d6bSBaptiste Daroussin dbadd_mlink(const struct mlink *mlink)
210261d06d6bSBaptiste Daroussin {
210361d06d6bSBaptiste Daroussin dba_page_alias(mlink->mpage->dba, mlink->name, NAME_FILE);
210461d06d6bSBaptiste Daroussin dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->dsec);
210561d06d6bSBaptiste Daroussin dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->fsec);
210661d06d6bSBaptiste Daroussin dba_page_add(mlink->mpage->dba, DBP_ARCH, mlink->arch);
210761d06d6bSBaptiste Daroussin dba_page_add(mlink->mpage->dba, DBP_FILE, mlink->file);
210861d06d6bSBaptiste Daroussin }
210961d06d6bSBaptiste Daroussin
211061d06d6bSBaptiste Daroussin /*
211161d06d6bSBaptiste Daroussin * Flush the current page's terms (and their bits) into the database.
211261d06d6bSBaptiste Daroussin * Also, handle escape sequences at the last possible moment.
211361d06d6bSBaptiste Daroussin */
211461d06d6bSBaptiste Daroussin static void
dbadd(struct dba * dba,struct mpage * mpage)211561d06d6bSBaptiste Daroussin dbadd(struct dba *dba, struct mpage *mpage)
211661d06d6bSBaptiste Daroussin {
211761d06d6bSBaptiste Daroussin struct mlink *mlink;
211861d06d6bSBaptiste Daroussin struct str *key;
211961d06d6bSBaptiste Daroussin char *cp;
212061d06d6bSBaptiste Daroussin uint64_t mask;
212161d06d6bSBaptiste Daroussin size_t i;
212261d06d6bSBaptiste Daroussin unsigned int slot;
212361d06d6bSBaptiste Daroussin int mustfree;
212461d06d6bSBaptiste Daroussin
212561d06d6bSBaptiste Daroussin mlink = mpage->mlinks;
212661d06d6bSBaptiste Daroussin
212761d06d6bSBaptiste Daroussin if (nodb) {
212861d06d6bSBaptiste Daroussin for (key = ohash_first(&names, &slot); NULL != key;
212961d06d6bSBaptiste Daroussin key = ohash_next(&names, &slot))
213061d06d6bSBaptiste Daroussin free(key);
213161d06d6bSBaptiste Daroussin for (key = ohash_first(&strings, &slot); NULL != key;
213261d06d6bSBaptiste Daroussin key = ohash_next(&strings, &slot))
213361d06d6bSBaptiste Daroussin free(key);
213461d06d6bSBaptiste Daroussin if (0 == debug)
213561d06d6bSBaptiste Daroussin return;
213661d06d6bSBaptiste Daroussin while (NULL != mlink) {
213761d06d6bSBaptiste Daroussin fputs(mlink->name, stdout);
213861d06d6bSBaptiste Daroussin if (NULL == mlink->next ||
213961d06d6bSBaptiste Daroussin strcmp(mlink->dsec, mlink->next->dsec) ||
214061d06d6bSBaptiste Daroussin strcmp(mlink->fsec, mlink->next->fsec) ||
214161d06d6bSBaptiste Daroussin strcmp(mlink->arch, mlink->next->arch)) {
214261d06d6bSBaptiste Daroussin putchar('(');
214361d06d6bSBaptiste Daroussin if ('\0' == *mlink->dsec)
214461d06d6bSBaptiste Daroussin fputs(mlink->fsec, stdout);
214561d06d6bSBaptiste Daroussin else
214661d06d6bSBaptiste Daroussin fputs(mlink->dsec, stdout);
214761d06d6bSBaptiste Daroussin if ('\0' != *mlink->arch)
214861d06d6bSBaptiste Daroussin printf("/%s", mlink->arch);
214961d06d6bSBaptiste Daroussin putchar(')');
215061d06d6bSBaptiste Daroussin }
215161d06d6bSBaptiste Daroussin mlink = mlink->next;
215261d06d6bSBaptiste Daroussin if (NULL != mlink)
215361d06d6bSBaptiste Daroussin fputs(", ", stdout);
215461d06d6bSBaptiste Daroussin }
215561d06d6bSBaptiste Daroussin printf(" - %s\n", mpage->desc);
215661d06d6bSBaptiste Daroussin return;
215761d06d6bSBaptiste Daroussin }
215861d06d6bSBaptiste Daroussin
215961d06d6bSBaptiste Daroussin if (debug)
216061d06d6bSBaptiste Daroussin say(mlink->file, "Adding to database");
216161d06d6bSBaptiste Daroussin
216261d06d6bSBaptiste Daroussin cp = mpage->desc;
216361d06d6bSBaptiste Daroussin i = strlen(cp);
216461d06d6bSBaptiste Daroussin mustfree = render_string(&cp, &i);
216561d06d6bSBaptiste Daroussin mpage->dba = dba_page_new(dba->pages,
216661d06d6bSBaptiste Daroussin *mpage->arch == '\0' ? mlink->arch : mpage->arch,
216761d06d6bSBaptiste Daroussin cp, mlink->file, mpage->form);
216861d06d6bSBaptiste Daroussin if (mustfree)
216961d06d6bSBaptiste Daroussin free(cp);
217061d06d6bSBaptiste Daroussin dba_page_add(mpage->dba, DBP_SECT, mpage->sec);
217161d06d6bSBaptiste Daroussin
217261d06d6bSBaptiste Daroussin while (mlink != NULL) {
217361d06d6bSBaptiste Daroussin dbadd_mlink(mlink);
217461d06d6bSBaptiste Daroussin mlink = mlink->next;
217561d06d6bSBaptiste Daroussin }
217661d06d6bSBaptiste Daroussin
217761d06d6bSBaptiste Daroussin for (key = ohash_first(&names, &slot); NULL != key;
217861d06d6bSBaptiste Daroussin key = ohash_next(&names, &slot)) {
217961d06d6bSBaptiste Daroussin assert(key->mpage == mpage);
218061d06d6bSBaptiste Daroussin dba_page_alias(mpage->dba, key->key, key->mask);
218161d06d6bSBaptiste Daroussin free(key);
218261d06d6bSBaptiste Daroussin }
218361d06d6bSBaptiste Daroussin for (key = ohash_first(&strings, &slot); NULL != key;
218461d06d6bSBaptiste Daroussin key = ohash_next(&strings, &slot)) {
218561d06d6bSBaptiste Daroussin assert(key->mpage == mpage);
218661d06d6bSBaptiste Daroussin i = 0;
218761d06d6bSBaptiste Daroussin for (mask = TYPE_Xr; mask <= TYPE_Lb; mask *= 2) {
218861d06d6bSBaptiste Daroussin if (key->mask & mask)
218961d06d6bSBaptiste Daroussin dba_macro_add(dba->macros, i,
219061d06d6bSBaptiste Daroussin key->key, mpage->dba);
219161d06d6bSBaptiste Daroussin i++;
219261d06d6bSBaptiste Daroussin }
219361d06d6bSBaptiste Daroussin free(key);
219461d06d6bSBaptiste Daroussin }
219561d06d6bSBaptiste Daroussin }
219661d06d6bSBaptiste Daroussin
219761d06d6bSBaptiste Daroussin static void
dbprune(struct dba * dba)219861d06d6bSBaptiste Daroussin dbprune(struct dba *dba)
219961d06d6bSBaptiste Daroussin {
220061d06d6bSBaptiste Daroussin struct dba_array *page, *files;
220161d06d6bSBaptiste Daroussin char *file;
220261d06d6bSBaptiste Daroussin
220361d06d6bSBaptiste Daroussin dba_array_FOREACH(dba->pages, page) {
220461d06d6bSBaptiste Daroussin files = dba_array_get(page, DBP_FILE);
220561d06d6bSBaptiste Daroussin dba_array_FOREACH(files, file) {
220661d06d6bSBaptiste Daroussin if (*file < ' ')
220761d06d6bSBaptiste Daroussin file++;
220861d06d6bSBaptiste Daroussin if (ohash_find(&mlinks, ohash_qlookup(&mlinks,
220961d06d6bSBaptiste Daroussin file)) != NULL) {
221061d06d6bSBaptiste Daroussin if (debug)
221161d06d6bSBaptiste Daroussin say(file, "Deleting from database");
221261d06d6bSBaptiste Daroussin dba_array_del(dba->pages);
221361d06d6bSBaptiste Daroussin break;
221461d06d6bSBaptiste Daroussin }
221561d06d6bSBaptiste Daroussin }
221661d06d6bSBaptiste Daroussin }
221761d06d6bSBaptiste Daroussin }
221861d06d6bSBaptiste Daroussin
221961d06d6bSBaptiste Daroussin /*
222061d06d6bSBaptiste Daroussin * Write the database from memory to disk.
222161d06d6bSBaptiste Daroussin */
222261d06d6bSBaptiste Daroussin static void
dbwrite(struct dba * dba)222361d06d6bSBaptiste Daroussin dbwrite(struct dba *dba)
222461d06d6bSBaptiste Daroussin {
222561d06d6bSBaptiste Daroussin struct stat sb1, sb2;
222661d06d6bSBaptiste Daroussin char tfn[33], *cp1, *cp2;
222761d06d6bSBaptiste Daroussin off_t i;
222861d06d6bSBaptiste Daroussin int fd1, fd2;
222961d06d6bSBaptiste Daroussin
223061d06d6bSBaptiste Daroussin /*
223161d06d6bSBaptiste Daroussin * Do not write empty databases, and delete existing ones
223261d06d6bSBaptiste Daroussin * when makewhatis -u causes them to become empty.
223361d06d6bSBaptiste Daroussin */
223461d06d6bSBaptiste Daroussin
223561d06d6bSBaptiste Daroussin dba_array_start(dba->pages);
223661d06d6bSBaptiste Daroussin if (dba_array_next(dba->pages) == NULL) {
223761d06d6bSBaptiste Daroussin if (unlink(MANDOC_DB) == -1 && errno != ENOENT)
223861d06d6bSBaptiste Daroussin say(MANDOC_DB, "&unlink");
223961d06d6bSBaptiste Daroussin return;
224061d06d6bSBaptiste Daroussin }
224161d06d6bSBaptiste Daroussin
224261d06d6bSBaptiste Daroussin /*
224361d06d6bSBaptiste Daroussin * Build the database in a temporary file,
224461d06d6bSBaptiste Daroussin * then atomically move it into place.
224561d06d6bSBaptiste Daroussin */
224661d06d6bSBaptiste Daroussin
224761d06d6bSBaptiste Daroussin if (dba_write(MANDOC_DB "~", dba) != -1) {
224861d06d6bSBaptiste Daroussin if (rename(MANDOC_DB "~", MANDOC_DB) == -1) {
224961d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
225061d06d6bSBaptiste Daroussin say(MANDOC_DB, "&rename");
225161d06d6bSBaptiste Daroussin unlink(MANDOC_DB "~");
225261d06d6bSBaptiste Daroussin }
225361d06d6bSBaptiste Daroussin return;
225461d06d6bSBaptiste Daroussin }
225561d06d6bSBaptiste Daroussin
225661d06d6bSBaptiste Daroussin /*
225761d06d6bSBaptiste Daroussin * We lack write permission and cannot replace the database
225861d06d6bSBaptiste Daroussin * file, but let's at least check whether the data changed.
225961d06d6bSBaptiste Daroussin */
226061d06d6bSBaptiste Daroussin
226161d06d6bSBaptiste Daroussin (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn));
226261d06d6bSBaptiste Daroussin if (mkdtemp(tfn) == NULL) {
226361d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
226461d06d6bSBaptiste Daroussin say("", "&%s", tfn);
226561d06d6bSBaptiste Daroussin return;
226661d06d6bSBaptiste Daroussin }
226761d06d6bSBaptiste Daroussin cp1 = cp2 = MAP_FAILED;
226861d06d6bSBaptiste Daroussin fd1 = fd2 = -1;
226961d06d6bSBaptiste Daroussin (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn));
227061d06d6bSBaptiste Daroussin if (dba_write(tfn, dba) == -1) {
227161d06d6bSBaptiste Daroussin say(tfn, "&dba_write");
227261d06d6bSBaptiste Daroussin goto err;
227361d06d6bSBaptiste Daroussin }
2274c1c95addSBrooks Davis if ((fd1 = open(MANDOC_DB, O_RDONLY)) == -1) {
227561d06d6bSBaptiste Daroussin say(MANDOC_DB, "&open");
227661d06d6bSBaptiste Daroussin goto err;
227761d06d6bSBaptiste Daroussin }
2278c1c95addSBrooks Davis if ((fd2 = open(tfn, O_RDONLY)) == -1) {
227961d06d6bSBaptiste Daroussin say(tfn, "&open");
228061d06d6bSBaptiste Daroussin goto err;
228161d06d6bSBaptiste Daroussin }
228261d06d6bSBaptiste Daroussin if (fstat(fd1, &sb1) == -1) {
228361d06d6bSBaptiste Daroussin say(MANDOC_DB, "&fstat");
228461d06d6bSBaptiste Daroussin goto err;
228561d06d6bSBaptiste Daroussin }
228661d06d6bSBaptiste Daroussin if (fstat(fd2, &sb2) == -1) {
228761d06d6bSBaptiste Daroussin say(tfn, "&fstat");
228861d06d6bSBaptiste Daroussin goto err;
228961d06d6bSBaptiste Daroussin }
229061d06d6bSBaptiste Daroussin if (sb1.st_size != sb2.st_size)
229161d06d6bSBaptiste Daroussin goto err;
229261d06d6bSBaptiste Daroussin if ((cp1 = mmap(NULL, sb1.st_size, PROT_READ, MAP_PRIVATE,
229361d06d6bSBaptiste Daroussin fd1, 0)) == MAP_FAILED) {
229461d06d6bSBaptiste Daroussin say(MANDOC_DB, "&mmap");
229561d06d6bSBaptiste Daroussin goto err;
229661d06d6bSBaptiste Daroussin }
229761d06d6bSBaptiste Daroussin if ((cp2 = mmap(NULL, sb2.st_size, PROT_READ, MAP_PRIVATE,
229861d06d6bSBaptiste Daroussin fd2, 0)) == MAP_FAILED) {
229961d06d6bSBaptiste Daroussin say(tfn, "&mmap");
230061d06d6bSBaptiste Daroussin goto err;
230161d06d6bSBaptiste Daroussin }
230261d06d6bSBaptiste Daroussin for (i = 0; i < sb1.st_size; i++)
230361d06d6bSBaptiste Daroussin if (cp1[i] != cp2[i])
230461d06d6bSBaptiste Daroussin goto err;
230561d06d6bSBaptiste Daroussin goto out;
230661d06d6bSBaptiste Daroussin
230761d06d6bSBaptiste Daroussin err:
230861d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
230961d06d6bSBaptiste Daroussin say(MANDOC_DB, "Data changed, but cannot replace database");
231061d06d6bSBaptiste Daroussin
231161d06d6bSBaptiste Daroussin out:
231261d06d6bSBaptiste Daroussin if (cp1 != MAP_FAILED)
231361d06d6bSBaptiste Daroussin munmap(cp1, sb1.st_size);
231461d06d6bSBaptiste Daroussin if (cp2 != MAP_FAILED)
231561d06d6bSBaptiste Daroussin munmap(cp2, sb2.st_size);
231661d06d6bSBaptiste Daroussin if (fd1 != -1)
231761d06d6bSBaptiste Daroussin close(fd1);
231861d06d6bSBaptiste Daroussin if (fd2 != -1)
231961d06d6bSBaptiste Daroussin close(fd2);
232061d06d6bSBaptiste Daroussin unlink(tfn);
232161d06d6bSBaptiste Daroussin *strrchr(tfn, '/') = '\0';
232261d06d6bSBaptiste Daroussin rmdir(tfn);
232361d06d6bSBaptiste Daroussin }
232461d06d6bSBaptiste Daroussin
232561d06d6bSBaptiste Daroussin static int
set_basedir(const char * targetdir,int report_baddir)232661d06d6bSBaptiste Daroussin set_basedir(const char *targetdir, int report_baddir)
232761d06d6bSBaptiste Daroussin {
232861d06d6bSBaptiste Daroussin static char startdir[PATH_MAX];
232961d06d6bSBaptiste Daroussin static int getcwd_status; /* 1 = ok, 2 = failure */
233061d06d6bSBaptiste Daroussin static int chdir_status; /* 1 = changed directory */
233161d06d6bSBaptiste Daroussin
233261d06d6bSBaptiste Daroussin /*
233361d06d6bSBaptiste Daroussin * Remember the original working directory, if possible.
233461d06d6bSBaptiste Daroussin * This will be needed if the second or a later directory
233561d06d6bSBaptiste Daroussin * on the command line is given as a relative path.
233661d06d6bSBaptiste Daroussin * Do not error out if the current directory is not
233761d06d6bSBaptiste Daroussin * searchable: Maybe it won't be needed after all.
233861d06d6bSBaptiste Daroussin */
23396d38604fSBaptiste Daroussin if (getcwd_status == 0) {
23406d38604fSBaptiste Daroussin if (getcwd(startdir, sizeof(startdir)) == NULL) {
234161d06d6bSBaptiste Daroussin getcwd_status = 2;
234261d06d6bSBaptiste Daroussin (void)strlcpy(startdir, strerror(errno),
234361d06d6bSBaptiste Daroussin sizeof(startdir));
234461d06d6bSBaptiste Daroussin } else
234561d06d6bSBaptiste Daroussin getcwd_status = 1;
234661d06d6bSBaptiste Daroussin }
234761d06d6bSBaptiste Daroussin
234861d06d6bSBaptiste Daroussin /*
234961d06d6bSBaptiste Daroussin * We are leaving the old base directory.
235061d06d6bSBaptiste Daroussin * Do not use it any longer, not even for messages.
235161d06d6bSBaptiste Daroussin */
235261d06d6bSBaptiste Daroussin *basedir = '\0';
23536d38604fSBaptiste Daroussin basedir_len = 0;
235461d06d6bSBaptiste Daroussin
235561d06d6bSBaptiste Daroussin /*
235661d06d6bSBaptiste Daroussin * If and only if the directory was changed earlier and
235761d06d6bSBaptiste Daroussin * the next directory to process is given as a relative path,
235861d06d6bSBaptiste Daroussin * first go back, or bail out if that is impossible.
235961d06d6bSBaptiste Daroussin */
23606d38604fSBaptiste Daroussin if (chdir_status && *targetdir != '/') {
23616d38604fSBaptiste Daroussin if (getcwd_status == 2) {
236261d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
236361d06d6bSBaptiste Daroussin say("", "getcwd: %s", startdir);
236461d06d6bSBaptiste Daroussin return 0;
236561d06d6bSBaptiste Daroussin }
23666d38604fSBaptiste Daroussin if (chdir(startdir) == -1) {
236761d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
236861d06d6bSBaptiste Daroussin say("", "&chdir %s", startdir);
236961d06d6bSBaptiste Daroussin return 0;
237061d06d6bSBaptiste Daroussin }
237161d06d6bSBaptiste Daroussin }
237261d06d6bSBaptiste Daroussin
237361d06d6bSBaptiste Daroussin /*
237461d06d6bSBaptiste Daroussin * Always resolve basedir to the canonicalized absolute
237561d06d6bSBaptiste Daroussin * pathname and append a trailing slash, such that
237661d06d6bSBaptiste Daroussin * we can reliably check whether files are inside.
237761d06d6bSBaptiste Daroussin */
23786d38604fSBaptiste Daroussin if (realpath(targetdir, basedir) == NULL) {
237961d06d6bSBaptiste Daroussin if (report_baddir || errno != ENOENT) {
238061d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
238161d06d6bSBaptiste Daroussin say("", "&%s: realpath", targetdir);
238261d06d6bSBaptiste Daroussin }
23836d38604fSBaptiste Daroussin *basedir = '\0';
238461d06d6bSBaptiste Daroussin return 0;
23856d38604fSBaptiste Daroussin } else if (chdir(basedir) == -1) {
238661d06d6bSBaptiste Daroussin if (report_baddir || errno != ENOENT) {
238761d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_BADARG;
238861d06d6bSBaptiste Daroussin say("", "&chdir");
238961d06d6bSBaptiste Daroussin }
23906d38604fSBaptiste Daroussin *basedir = '\0';
239161d06d6bSBaptiste Daroussin return 0;
239261d06d6bSBaptiste Daroussin }
239361d06d6bSBaptiste Daroussin chdir_status = 1;
23946d38604fSBaptiste Daroussin basedir_len = strlen(basedir);
23956d38604fSBaptiste Daroussin if (basedir[basedir_len - 1] != '/') {
23966d38604fSBaptiste Daroussin if (basedir_len >= PATH_MAX - 1) {
239761d06d6bSBaptiste Daroussin exitcode = (int)MANDOCLEVEL_SYSERR;
239861d06d6bSBaptiste Daroussin say("", "Filename too long");
23996d38604fSBaptiste Daroussin *basedir = '\0';
24006d38604fSBaptiste Daroussin basedir_len = 0;
240161d06d6bSBaptiste Daroussin return 0;
240261d06d6bSBaptiste Daroussin }
24036d38604fSBaptiste Daroussin basedir[basedir_len++] = '/';
24046d38604fSBaptiste Daroussin basedir[basedir_len] = '\0';
240561d06d6bSBaptiste Daroussin }
240661d06d6bSBaptiste Daroussin return 1;
240761d06d6bSBaptiste Daroussin }
240861d06d6bSBaptiste Daroussin
24096d38604fSBaptiste Daroussin #ifdef READ_ALLOWED_PATH
24106d38604fSBaptiste Daroussin static int
read_allowed(const char * candidate)24116d38604fSBaptiste Daroussin read_allowed(const char *candidate)
24126d38604fSBaptiste Daroussin {
24136d38604fSBaptiste Daroussin const char *cp;
24146d38604fSBaptiste Daroussin size_t len;
24156d38604fSBaptiste Daroussin
24166d38604fSBaptiste Daroussin for (cp = READ_ALLOWED_PATH;; cp += len) {
24176d38604fSBaptiste Daroussin while (*cp == ':')
24186d38604fSBaptiste Daroussin cp++;
24196d38604fSBaptiste Daroussin if (*cp == '\0')
24206d38604fSBaptiste Daroussin return 0;
24216d38604fSBaptiste Daroussin len = strcspn(cp, ":");
24226d38604fSBaptiste Daroussin if (strncmp(candidate, cp, len) == 0)
24236d38604fSBaptiste Daroussin return 1;
24246d38604fSBaptiste Daroussin }
24256d38604fSBaptiste Daroussin }
24266d38604fSBaptiste Daroussin #endif
24276d38604fSBaptiste Daroussin
242861d06d6bSBaptiste Daroussin static void
say(const char * file,const char * format,...)242961d06d6bSBaptiste Daroussin say(const char *file, const char *format, ...)
243061d06d6bSBaptiste Daroussin {
243161d06d6bSBaptiste Daroussin va_list ap;
243261d06d6bSBaptiste Daroussin int use_errno;
243361d06d6bSBaptiste Daroussin
24346d38604fSBaptiste Daroussin if (*basedir != '\0')
243561d06d6bSBaptiste Daroussin fprintf(stderr, "%s", basedir);
24366d38604fSBaptiste Daroussin if (*basedir != '\0' && *file != '\0')
243761d06d6bSBaptiste Daroussin fputc('/', stderr);
24386d38604fSBaptiste Daroussin if (*file != '\0')
243961d06d6bSBaptiste Daroussin fprintf(stderr, "%s", file);
244061d06d6bSBaptiste Daroussin
244161d06d6bSBaptiste Daroussin use_errno = 1;
24426d38604fSBaptiste Daroussin if (format != NULL) {
244361d06d6bSBaptiste Daroussin switch (*format) {
244461d06d6bSBaptiste Daroussin case '&':
244561d06d6bSBaptiste Daroussin format++;
244661d06d6bSBaptiste Daroussin break;
244761d06d6bSBaptiste Daroussin case '\0':
244861d06d6bSBaptiste Daroussin format = NULL;
244961d06d6bSBaptiste Daroussin break;
245061d06d6bSBaptiste Daroussin default:
245161d06d6bSBaptiste Daroussin use_errno = 0;
245261d06d6bSBaptiste Daroussin break;
245361d06d6bSBaptiste Daroussin }
245461d06d6bSBaptiste Daroussin }
24556d38604fSBaptiste Daroussin if (format != NULL) {
24566d38604fSBaptiste Daroussin if (*basedir != '\0' || *file != '\0')
245761d06d6bSBaptiste Daroussin fputs(": ", stderr);
245861d06d6bSBaptiste Daroussin va_start(ap, format);
245961d06d6bSBaptiste Daroussin vfprintf(stderr, format, ap);
246061d06d6bSBaptiste Daroussin va_end(ap);
246161d06d6bSBaptiste Daroussin }
246261d06d6bSBaptiste Daroussin if (use_errno) {
24636d38604fSBaptiste Daroussin if (*basedir != '\0' || *file != '\0' || format != NULL)
246461d06d6bSBaptiste Daroussin fputs(": ", stderr);
246561d06d6bSBaptiste Daroussin perror(NULL);
246661d06d6bSBaptiste Daroussin } else
246761d06d6bSBaptiste Daroussin fputc('\n', stderr);
246861d06d6bSBaptiste Daroussin }
2469