version 1.260, 2018/12/13 11:55:46 |
version 1.273, 2024/05/14 18:52:31 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
|
* Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org> |
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org> |
|
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org> |
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
* |
|
* Implementation of the makewhatis(8) program. |
*/ |
*/ |
#include "config.h" |
#include "config.h" |
|
|
Line 118 struct mdoc_handler { |
|
Line 120 struct mdoc_handler { |
|
int mandocdb(int, char *[]); |
int mandocdb(int, char *[]); |
|
|
static void dbadd(struct dba *, struct mpage *); |
static void dbadd(struct dba *, struct mpage *); |
static void dbadd_mlink(const struct mlink *mlink); |
static void dbadd_mlink(const struct mlink *); |
static void dbprune(struct dba *); |
static void dbprune(struct dba *); |
static void dbwrite(struct dba *); |
static void dbwrite(struct dba *); |
static void filescan(const char *); |
static void filescan(const char *); |
Line 163 static void putkey(const struct mpage *, char *, uint |
|
Line 165 static void putkey(const struct mpage *, char *, uint |
|
static void putkeys(const struct mpage *, char *, size_t, uint64_t); |
static void putkeys(const struct mpage *, char *, size_t, uint64_t); |
static void putmdockey(const struct mpage *, |
static void putmdockey(const struct mpage *, |
const struct roff_node *, uint64_t, int); |
const struct roff_node *, uint64_t, int); |
|
#ifdef READ_ALLOWED_PATH |
|
static int read_allowed(const char *); |
|
#endif |
static int render_string(char **, size_t *); |
static int render_string(char **, size_t *); |
static void say(const char *, const char *, ...) |
static void say(const char *, const char *, ...) |
__attribute__((__format__ (__printf__, 2, 3))); |
__attribute__((__format__ (__printf__, 2, 3))); |
Line 179 static int write_utf8; /* write UTF-8 output; else A |
|
Line 184 static int write_utf8; /* write UTF-8 output; else A |
|
static int exitcode; /* to be returned by main */ |
static int exitcode; /* to be returned by main */ |
static enum op op; /* operational mode */ |
static enum op op; /* operational mode */ |
static char basedir[PATH_MAX]; /* current base directory */ |
static char basedir[PATH_MAX]; /* current base directory */ |
|
static size_t basedir_len; /* strlen(basedir) */ |
static struct mpage *mpage_head; /* list of distinct manual pages */ |
static struct mpage *mpage_head; /* list of distinct manual pages */ |
static struct ohash mpages; /* table of distinct manual pages */ |
static struct ohash mpages; /* table of distinct manual pages */ |
static struct ohash mlinks; /* table of directory entries */ |
static struct ohash mlinks; /* table of directory entries */ |
Line 342 mandocdb(int argc, char *argv[]) |
|
Line 348 mandocdb(int argc, char *argv[]) |
|
* clobber each other. |
* clobber each other. |
*/ |
*/ |
#define CHECKOP(_op, _ch) do \ |
#define CHECKOP(_op, _ch) do \ |
if (OP_DEFAULT != (_op)) { \ |
if ((_op) != OP_DEFAULT) { \ |
warnx("-%c: Conflicting option", (_ch)); \ |
warnx("-%c: Conflicting option", (_ch)); \ |
goto usage; \ |
goto usage; \ |
} while (/*CONSTCOND*/0) |
} while (/*CONSTCOND*/0) |
|
|
|
mparse_options = MPARSE_UTF8 | MPARSE_LATIN1 | MPARSE_VALIDATE; |
path_arg = NULL; |
path_arg = NULL; |
op = OP_DEFAULT; |
op = OP_DEFAULT; |
|
|
while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v"))) |
while ((ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")) != -1) |
switch (ch) { |
switch (ch) { |
case 'a': |
case 'a': |
use_all = 1; |
use_all = 1; |
Line 378 mandocdb(int argc, char *argv[]) |
|
Line 385 mandocdb(int argc, char *argv[]) |
|
mparse_options |= MPARSE_QUICK; |
mparse_options |= MPARSE_QUICK; |
break; |
break; |
case 'T': |
case 'T': |
if (strcmp(optarg, "utf8")) { |
if (strcmp(optarg, "utf8") != 0) { |
warnx("-T%s: Unsupported output format", |
warnx("-T%s: Unsupported output format", |
optarg); |
optarg); |
goto usage; |
goto usage; |
Line 415 mandocdb(int argc, char *argv[]) |
|
Line 422 mandocdb(int argc, char *argv[]) |
|
} |
} |
#endif |
#endif |
|
|
if (OP_CONFFILE == op && argc > 0) { |
if (op == OP_CONFFILE && argc > 0) { |
warnx("-C: Too many arguments"); |
warnx("-C: Too many arguments"); |
goto usage; |
goto usage; |
} |
} |
|
|
exitcode = (int)MANDOCLEVEL_OK; |
exitcode = (int)MANDOCLEVEL_OK; |
mchars_alloc(); |
mchars_alloc(); |
mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL, |
mp = mparse_alloc(mparse_options, MANDOC_OS_OTHER, NULL); |
MANDOC_OS_OTHER, NULL); |
|
mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); |
mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); |
mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); |
mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); |
|
|
if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { |
if (op == OP_UPDATE || op == OP_DELETE || op == OP_TEST) { |
|
|
/* |
/* |
* Most of these deal with a specific directory. |
* Most of these deal with a specific directory. |
* Jump into that directory first. |
* Jump into that directory first. |
*/ |
*/ |
if (OP_TEST != op && 0 == set_basedir(path_arg, 1)) |
if (op != OP_TEST && set_basedir(path_arg, 1) == 0) |
goto out; |
goto out; |
|
|
dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); |
dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); |
Line 454 mandocdb(int argc, char *argv[]) |
|
Line 460 mandocdb(int argc, char *argv[]) |
|
" from scratch", strerror(errno)); |
" from scratch", strerror(errno)); |
exitcode = (int)MANDOCLEVEL_OK; |
exitcode = (int)MANDOCLEVEL_OK; |
op = OP_DEFAULT; |
op = OP_DEFAULT; |
if (0 == treescan()) |
if (treescan() == 0) |
goto out; |
goto out; |
dba = dba_new(128); |
dba = dba_new(128); |
} |
} |
if (OP_DELETE != op) |
if (op != OP_DELETE) |
mpages_merge(dba, mp); |
mpages_merge(dba, mp); |
if (nodb == 0) |
if (nodb == 0) |
dbwrite(dba); |
dbwrite(dba); |
Line 492 mandocdb(int argc, char *argv[]) |
|
Line 498 mandocdb(int argc, char *argv[]) |
|
sz = strlen(conf.manpath.paths[j]); |
sz = strlen(conf.manpath.paths[j]); |
if (sz && conf.manpath.paths[j][sz - 1] == '/') |
if (sz && conf.manpath.paths[j][sz - 1] == '/') |
conf.manpath.paths[j][--sz] = '\0'; |
conf.manpath.paths[j][--sz] = '\0'; |
if (0 == sz) |
if (sz == 0) |
continue; |
continue; |
|
|
if (j) { |
if (j) { |
Line 502 mandocdb(int argc, char *argv[]) |
|
Line 508 mandocdb(int argc, char *argv[]) |
|
offsetof(struct mlink, file)); |
offsetof(struct mlink, file)); |
} |
} |
|
|
if ( ! set_basedir(conf.manpath.paths[j], argc > 0)) |
if (set_basedir(conf.manpath.paths[j], argc > 0) == 0) |
continue; |
continue; |
if (0 == treescan()) |
if (treescan() == 0) |
continue; |
continue; |
dba = dba_new(128); |
dba = dba_new(128); |
mpages_merge(dba, mp); |
mpages_merge(dba, mp); |
|
|
mpages_free(); |
mpages_free(); |
ohash_delete(&mpages); |
ohash_delete(&mpages); |
ohash_delete(&mlinks); |
ohash_delete(&mlinks); |
|
#if DEBUG_MEMORY |
|
mandoc_dbg_finish(); |
|
#endif |
return exitcode; |
return exitcode; |
usage: |
usage: |
progname = getprogname(); |
progname = getprogname(); |
|
|
say(path, "&realpath"); |
say(path, "&realpath"); |
continue; |
continue; |
} |
} |
if (strstr(buf, basedir) != buf |
if (strncmp(buf, basedir, basedir_len) != 0 |
#ifdef HOMEBREWDIR |
#ifdef READ_ALLOWED_PATH |
&& strstr(buf, HOMEBREWDIR) != buf |
&& !read_allowed(buf) |
#endif |
#endif |
) { |
) { |
if (warnings) say("", |
if (warnings) say("", |
|
|
say(path, "&stat"); |
say(path, "&stat"); |
continue; |
continue; |
} |
} |
|
if ((ff->fts_statp->st_mode & S_IFMT) != S_IFREG) |
|
continue; |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
|
|
/* |
/* |
|
|
* See treescan() for the fts(3) version of this. |
* See treescan() for the fts(3) version of this. |
*/ |
*/ |
static void |
static void |
filescan(const char *file) |
filescan(const char *infile) |
{ |
{ |
char buf[PATH_MAX]; |
|
struct stat st; |
struct stat st; |
struct mlink *mlink; |
struct mlink *mlink; |
char *p, *start; |
char *linkfile, *p, *realdir, *start, *usefile; |
|
size_t realdir_len; |
|
|
assert(use_all); |
assert(use_all); |
|
|
if (0 == strncmp(file, "./", 2)) |
if (strncmp(infile, "./", 2) == 0) |
file += 2; |
infile += 2; |
|
|
/* |
/* |
* We have to do lstat(2) before realpath(3) loses |
* We have to do lstat(2) before realpath(3) loses |
* the information whether this is a symbolic link. |
* the information whether this is a symbolic link. |
* We need to know that because for symbolic links, |
* We need to know that because for symbolic links, |
* we want to use the orginal file name, while for |
* we want to use the original file name, while for |
* regular files, we want to use the real path. |
* regular files, we want to use the real path. |
*/ |
*/ |
if (-1 == lstat(file, &st)) { |
if (lstat(infile, &st) == -1) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&lstat"); |
say(infile, "&lstat"); |
return; |
return; |
} else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { |
} else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "Not a regular file"); |
say(infile, "Not a regular file"); |
return; |
return; |
} |
} |
|
|
Line 810 filescan(const char *file) |
|
Line 821 filescan(const char *file) |
|
* We have to resolve the file name to the real path |
* We have to resolve the file name to the real path |
* in any case for the base directory check. |
* in any case for the base directory check. |
*/ |
*/ |
if (NULL == realpath(file, buf)) { |
if ((usefile = realpath(infile, NULL)) == NULL) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&realpath"); |
say(infile, "&realpath"); |
return; |
return; |
} |
} |
|
|
if (OP_TEST == op) |
if (op == OP_TEST) |
start = buf; |
start = usefile; |
else if (strstr(buf, basedir) == buf) |
else if (strncmp(usefile, basedir, basedir_len) == 0) |
start = buf + strlen(basedir); |
start = usefile + basedir_len; |
#ifdef HOMEBREWDIR |
#ifdef READ_ALLOWED_PATH |
else if (strstr(buf, HOMEBREWDIR) == buf) |
else if (read_allowed(usefile)) |
start = buf; |
start = usefile; |
#endif |
#endif |
else { |
else { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "%s: outside base directory", buf); |
say("", "%s: outside base directory", infile); |
|
free(usefile); |
return; |
return; |
} |
} |
|
|
Line 834 filescan(const char *file) |
|
Line 846 filescan(const char *file) |
|
* Now we are sure the file is inside our tree. |
* Now we are sure the file is inside our tree. |
* If it is a symbolic link, ignore the real path |
* If it is a symbolic link, ignore the real path |
* and use the original name. |
* and use the original name. |
* This implies passing stuff like "cat1/../man1/foo.1" |
|
* on the command line won't work. So don't do that. |
|
* Note the stat(2) can still fail if the link target |
|
* doesn't exist. |
|
*/ |
*/ |
if (S_IFLNK & st.st_mode) { |
do { |
if (-1 == stat(buf, &st)) { |
if (S_ISLNK(st.st_mode) == 0) |
|
break; |
|
|
|
/* |
|
* Some implementations of realpath(3) may succeed |
|
* even if the target of the link does not exist, |
|
* so check again for extra safety. |
|
*/ |
|
if (stat(usefile, &st) == -1) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say(file, "&stat"); |
say(infile, "&stat"); |
|
free(usefile); |
return; |
return; |
} |
} |
if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { |
linkfile = mandoc_strdup(infile); |
say(file, "Filename too long"); |
if (op == OP_TEST) { |
return; |
free(usefile); |
|
start = usefile = linkfile; |
|
break; |
} |
} |
start = buf; |
if (strncmp(infile, basedir, basedir_len) == 0) { |
if (OP_TEST != op && strstr(buf, basedir) == buf) |
free(usefile); |
start += strlen(basedir); |
usefile = linkfile; |
} |
start = usefile + basedir_len; |
|
break; |
|
} |
|
|
|
/* |
|
* This symbolic link points into the basedir |
|
* from the outside. Let's see whether any of |
|
* the parent directories resolve to the basedir. |
|
*/ |
|
p = strchr(linkfile, '\0'); |
|
do { |
|
while (*--p != '/') |
|
continue; |
|
*p = '\0'; |
|
if ((realdir = realpath(linkfile, NULL)) == NULL) { |
|
exitcode = (int)MANDOCLEVEL_BADARG; |
|
say(infile, "&realpath"); |
|
free(linkfile); |
|
free(usefile); |
|
return; |
|
} |
|
realdir_len = strlen(realdir) + 1; |
|
free(realdir); |
|
*p = '/'; |
|
} while (realdir_len > basedir_len); |
|
|
|
/* |
|
* If one of the directories resolves to the basedir, |
|
* use the rest of the original name. |
|
* Otherwise, the best we can do |
|
* is to use the filename pointed to. |
|
*/ |
|
if (realdir_len == basedir_len) { |
|
free(usefile); |
|
usefile = linkfile; |
|
start = p + 1; |
|
} else { |
|
free(linkfile); |
|
start = usefile + basedir_len; |
|
} |
|
} while (/* CONSTCOND */ 0); |
|
|
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
mlink->dform = FORM_NONE; |
mlink->dform = FORM_NONE; |
if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= |
if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= |
sizeof(mlink->file)) { |
sizeof(mlink->file)) { |
say(start, "Filename too long"); |
say(start, "Filename too long"); |
free(mlink); |
free(mlink); |
|
free(usefile); |
return; |
return; |
} |
} |
|
|
Line 868 filescan(const char *file) |
|
Line 928 filescan(const char *file) |
|
* but outside our tree, guess the base directory. |
* but outside our tree, guess the base directory. |
*/ |
*/ |
|
|
if (op == OP_TEST || (start == buf && *start == '/')) { |
if (op == OP_TEST || (start == usefile && *start == '/')) { |
if (strncmp(buf, "man/", 4) == 0) |
if (strncmp(usefile, "man/", 4) == 0) |
start = buf + 4; |
start = usefile + 4; |
else if ((start = strstr(buf, "/man/")) != NULL) |
else if ((start = strstr(usefile, "/man/")) != NULL) |
start += 5; |
start += 5; |
else |
else |
start = buf; |
start = usefile; |
} |
} |
|
|
/* |
/* |
Line 883 filescan(const char *file) |
|
Line 943 filescan(const char *file) |
|
* If we find one of these and what's underneath is a directory, |
* If we find one of these and what's underneath is a directory, |
* assume it's an architecture. |
* assume it's an architecture. |
*/ |
*/ |
if (NULL != (p = strchr(start, '/'))) { |
if ((p = strchr(start, '/')) != NULL) { |
*p++ = '\0'; |
*p++ = '\0'; |
if (0 == strncmp(start, "man", 3)) { |
if (strncmp(start, "man", 3) == 0) { |
mlink->dform = FORM_SRC; |
mlink->dform = FORM_SRC; |
mlink->dsec = start + 3; |
mlink->dsec = start + 3; |
} else if (0 == strncmp(start, "cat", 3)) { |
} else if (strncmp(start, "cat", 3) == 0) { |
mlink->dform = FORM_CAT; |
mlink->dform = FORM_CAT; |
mlink->dsec = start + 3; |
mlink->dsec = start + 3; |
} |
} |
|
|
start = p; |
start = p; |
if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { |
if (mlink->dsec != NULL && (p = strchr(start, '/')) != NULL) { |
*p++ = '\0'; |
*p++ = '\0'; |
mlink->arch = start; |
mlink->arch = start; |
start = p; |
start = p; |
Line 906 filescan(const char *file) |
|
Line 966 filescan(const char *file) |
|
* Suffix of `.0' indicates a catpage, `.1-9' is a manpage. |
* Suffix of `.0' indicates a catpage, `.1-9' is a manpage. |
*/ |
*/ |
p = strrchr(start, '\0'); |
p = strrchr(start, '\0'); |
while (p-- > start && '/' != *p && '.' != *p) |
while (p-- > start && *p != '/' && *p != '.') |
/* Loop. */ ; |
continue; |
|
|
if ('.' == *p) { |
if (*p == '.') { |
*p++ = '\0'; |
*p++ = '\0'; |
mlink->fsec = p; |
mlink->fsec = p; |
} |
} |
Line 919 filescan(const char *file) |
|
Line 979 filescan(const char *file) |
|
* Use the filename portion of the path. |
* Use the filename portion of the path. |
*/ |
*/ |
mlink->name = start; |
mlink->name = start; |
if (NULL != (p = strrchr(start, '/'))) { |
if ((p = strrchr(start, '/')) != NULL) { |
mlink->name = p + 1; |
mlink->name = p + 1; |
*p = '\0'; |
*p = '\0'; |
} |
} |
mlink_add(mlink, &st); |
mlink_add(mlink, &st); |
|
free(usefile); |
} |
} |
|
|
static void |
static void |
Line 1116 mpages_merge(struct dba *dba, struct mparse *mp) |
|
Line 1177 mpages_merge(struct dba *dba, struct mparse *mp) |
|
{ |
{ |
struct mpage *mpage, *mpage_dest; |
struct mpage *mpage, *mpage_dest; |
struct mlink *mlink, *mlink_dest; |
struct mlink *mlink, *mlink_dest; |
struct roff_man *man; |
struct roff_meta *meta; |
char *sodest; |
|
char *cp; |
char *cp; |
int fd; |
int fd; |
|
|
Line 1130 mpages_merge(struct dba *dba, struct mparse *mp) |
|
Line 1190 mpages_merge(struct dba *dba, struct mparse *mp) |
|
mandoc_ohash_init(&names, 4, offsetof(struct str, key)); |
mandoc_ohash_init(&names, 4, offsetof(struct str, key)); |
mandoc_ohash_init(&strings, 6, offsetof(struct str, key)); |
mandoc_ohash_init(&strings, 6, offsetof(struct str, key)); |
mparse_reset(mp); |
mparse_reset(mp); |
man = NULL; |
meta = NULL; |
sodest = NULL; |
|
|
|
if ((fd = mparse_open(mp, mlink->file)) == -1) { |
if ((fd = mparse_open(mp, mlink->file)) == -1) { |
say(mlink->file, "&open"); |
say(mlink->file, "&open"); |
Line 1146 mpages_merge(struct dba *dba, struct mparse *mp) |
|
Line 1205 mpages_merge(struct dba *dba, struct mparse *mp) |
|
mparse_readfd(mp, fd, mlink->file); |
mparse_readfd(mp, fd, mlink->file); |
close(fd); |
close(fd); |
fd = -1; |
fd = -1; |
mparse_result(mp, &man, &sodest); |
meta = mparse_result(mp); |
} |
} |
|
|
if (sodest != NULL) { |
if (meta != NULL && meta->sodest != NULL) { |
mlink_dest = ohash_find(&mlinks, |
mlink_dest = ohash_find(&mlinks, |
ohash_qlookup(&mlinks, sodest)); |
ohash_qlookup(&mlinks, meta->sodest)); |
if (mlink_dest == NULL) { |
if (mlink_dest == NULL) { |
mandoc_asprintf(&cp, "%s.gz", sodest); |
mandoc_asprintf(&cp, "%s.gz", meta->sodest); |
mlink_dest = ohash_find(&mlinks, |
mlink_dest = ohash_find(&mlinks, |
ohash_qlookup(&mlinks, cp)); |
ohash_qlookup(&mlinks, cp)); |
free(cp); |
free(cp); |
Line 1188 mpages_merge(struct dba *dba, struct mparse *mp) |
|
Line 1247 mpages_merge(struct dba *dba, struct mparse *mp) |
|
mlink->next = mlink_dest->next; |
mlink->next = mlink_dest->next; |
mlink_dest->next = mpage->mlinks; |
mlink_dest->next = mpage->mlinks; |
mpage->mlinks = NULL; |
mpage->mlinks = NULL; |
|
goto nextpage; |
} |
} |
goto nextpage; |
meta->macroset = MACROSET_NONE; |
} else if (man != NULL && man->macroset == MACROSET_MDOC) { |
} |
mdoc_validate(man); |
if (meta != NULL && meta->macroset == MACROSET_MDOC) { |
mpage->form = FORM_SRC; |
mpage->form = FORM_SRC; |
mpage->sec = man->meta.msec; |
mpage->sec = meta->msec; |
mpage->sec = mandoc_strdup( |
mpage->sec = mandoc_strdup( |
mpage->sec == NULL ? "" : mpage->sec); |
mpage->sec == NULL ? "" : mpage->sec); |
mpage->arch = man->meta.arch; |
mpage->arch = meta->arch; |
mpage->arch = mandoc_strdup( |
mpage->arch = mandoc_strdup( |
mpage->arch == NULL ? "" : mpage->arch); |
mpage->arch == NULL ? "" : mpage->arch); |
mpage->title = mandoc_strdup(man->meta.title); |
mpage->title = mandoc_strdup(meta->title); |
} else if (man != NULL && man->macroset == MACROSET_MAN) { |
} else if (meta != NULL && meta->macroset == MACROSET_MAN) { |
man_validate(man); |
if (*meta->msec != '\0' || *meta->title != '\0') { |
if (*man->meta.msec != '\0' || |
|
*man->meta.title != '\0') { |
|
mpage->form = FORM_SRC; |
mpage->form = FORM_SRC; |
mpage->sec = mandoc_strdup(man->meta.msec); |
mpage->sec = mandoc_strdup(meta->msec); |
mpage->arch = mandoc_strdup(mlink->arch); |
mpage->arch = mandoc_strdup(mlink->arch); |
mpage->title = mandoc_strdup(man->meta.title); |
mpage->title = mandoc_strdup(meta->title); |
} else |
} else |
man = NULL; |
meta = NULL; |
} |
} |
|
|
assert(mpage->desc == NULL); |
assert(mpage->desc == NULL); |
if (man == NULL) { |
if (meta == NULL || meta->sodest != NULL) { |
mpage->form = FORM_CAT; |
|
mpage->sec = mandoc_strdup(mlink->dsec); |
mpage->sec = mandoc_strdup(mlink->dsec); |
mpage->arch = mandoc_strdup(mlink->arch); |
mpage->arch = mandoc_strdup(mlink->arch); |
mpage->title = mandoc_strdup(mlink->name); |
mpage->title = mandoc_strdup(mlink->name); |
parse_cat(mpage, fd); |
if (meta == NULL) { |
} else if (man->macroset == MACROSET_MDOC) |
mpage->form = FORM_CAT; |
parse_mdoc(mpage, &man->meta, man->first); |
parse_cat(mpage, fd); |
|
} else |
|
mpage->form = FORM_SRC; |
|
} else if (meta->macroset == MACROSET_MDOC) |
|
parse_mdoc(mpage, meta, meta->first); |
else |
else |
parse_man(mpage, &man->meta, man->first); |
parse_man(mpage, meta, meta->first); |
if (mpage->desc == NULL) { |
if (mpage->desc == NULL) { |
mpage->desc = mandoc_strdup(mlink->name); |
mpage->desc = mandoc_strdup(mlink->name); |
if (warnings) |
if (warnings) |
Line 1970 render_string(char **public, size_t *psz) |
|
Line 2031 render_string(char **public, size_t *psz) |
|
*/ |
*/ |
|
|
scp++; |
scp++; |
if (mandoc_escape(&scp, &seq, &seqlen) != ESCAPE_SPECIAL) |
switch (mandoc_escape(&scp, &seq, &seqlen)) { |
|
case ESCAPE_UNICODE: |
|
unicode = mchars_num2uc(seq + 1, seqlen - 1); |
|
break; |
|
case ESCAPE_NUMBERED: |
|
unicode = mchars_num2char(seq, seqlen); |
|
break; |
|
case ESCAPE_SPECIAL: |
|
unicode = mchars_spec2cp(seq, seqlen); |
|
break; |
|
default: |
|
unicode = -1; |
|
break; |
|
} |
|
if (unicode <= 0) |
continue; |
continue; |
|
|
/* |
/* |
Line 1979 render_string(char **public, size_t *psz) |
|
Line 2054 render_string(char **public, size_t *psz) |
|
*/ |
*/ |
|
|
if (write_utf8) { |
if (write_utf8) { |
unicode = mchars_spec2cp(seq, seqlen); |
|
if (unicode <= 0) |
|
continue; |
|
addsz = utf8(unicode, utfbuf); |
addsz = utf8(unicode, utfbuf); |
if (addsz == 0) |
if (addsz == 0) |
continue; |
continue; |
addcp = utfbuf; |
addcp = utfbuf; |
} else { |
} else { |
addcp = mchars_spec2str(seq, seqlen, &addsz); |
addcp = mchars_uc2str(unicode); |
if (addcp == NULL) |
if (addcp == NULL) |
continue; |
continue; |
if (*addcp == ASCII_NBRSP) { |
if (*addcp == ASCII_NBRSP) |
addcp = " "; |
addcp = " "; |
addsz = 1; |
addsz = strlen(addcp); |
} |
|
} |
} |
|
|
/* Copy the rendered glyph into the stream. */ |
/* Copy the rendered glyph into the stream. */ |
Line 2193 dbwrite(struct dba *dba) |
|
Line 2264 dbwrite(struct dba *dba) |
|
say(tfn, "&dba_write"); |
say(tfn, "&dba_write"); |
goto err; |
goto err; |
} |
} |
if ((fd1 = open(MANDOC_DB, O_RDONLY, 0)) == -1) { |
if ((fd1 = open(MANDOC_DB, O_RDONLY)) == -1) { |
say(MANDOC_DB, "&open"); |
say(MANDOC_DB, "&open"); |
goto err; |
goto err; |
} |
} |
if ((fd2 = open(tfn, O_RDONLY, 0)) == -1) { |
if ((fd2 = open(tfn, O_RDONLY)) == -1) { |
say(tfn, "&open"); |
say(tfn, "&open"); |
goto err; |
goto err; |
} |
} |
Line 2250 set_basedir(const char *targetdir, int report_baddir) |
|
Line 2321 set_basedir(const char *targetdir, int report_baddir) |
|
static char startdir[PATH_MAX]; |
static char startdir[PATH_MAX]; |
static int getcwd_status; /* 1 = ok, 2 = failure */ |
static int getcwd_status; /* 1 = ok, 2 = failure */ |
static int chdir_status; /* 1 = changed directory */ |
static int chdir_status; /* 1 = changed directory */ |
char *cp; |
|
|
|
/* |
/* |
* Remember the original working directory, if possible. |
* Remember the original working directory, if possible. |
Line 2259 set_basedir(const char *targetdir, int report_baddir) |
|
Line 2329 set_basedir(const char *targetdir, int report_baddir) |
|
* Do not error out if the current directory is not |
* Do not error out if the current directory is not |
* searchable: Maybe it won't be needed after all. |
* searchable: Maybe it won't be needed after all. |
*/ |
*/ |
if (0 == getcwd_status) { |
if (getcwd_status == 0) { |
if (NULL == getcwd(startdir, sizeof(startdir))) { |
if (getcwd(startdir, sizeof(startdir)) == NULL) { |
getcwd_status = 2; |
getcwd_status = 2; |
(void)strlcpy(startdir, strerror(errno), |
(void)strlcpy(startdir, strerror(errno), |
sizeof(startdir)); |
sizeof(startdir)); |
Line 2273 set_basedir(const char *targetdir, int report_baddir) |
|
Line 2343 set_basedir(const char *targetdir, int report_baddir) |
|
* Do not use it any longer, not even for messages. |
* Do not use it any longer, not even for messages. |
*/ |
*/ |
*basedir = '\0'; |
*basedir = '\0'; |
|
basedir_len = 0; |
|
|
/* |
/* |
* If and only if the directory was changed earlier and |
* If and only if the directory was changed earlier and |
* the next directory to process is given as a relative path, |
* the next directory to process is given as a relative path, |
* first go back, or bail out if that is impossible. |
* first go back, or bail out if that is impossible. |
*/ |
*/ |
if (chdir_status && '/' != *targetdir) { |
if (chdir_status && *targetdir != '/') { |
if (2 == getcwd_status) { |
if (getcwd_status == 2) { |
exitcode = (int)MANDOCLEVEL_SYSERR; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
say("", "getcwd: %s", startdir); |
say("", "getcwd: %s", startdir); |
return 0; |
return 0; |
} |
} |
if (-1 == chdir(startdir)) { |
if (chdir(startdir) == -1) { |
exitcode = (int)MANDOCLEVEL_SYSERR; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
say("", "&chdir %s", startdir); |
say("", "&chdir %s", startdir); |
return 0; |
return 0; |
Line 2297 set_basedir(const char *targetdir, int report_baddir) |
|
Line 2368 set_basedir(const char *targetdir, int report_baddir) |
|
* pathname and append a trailing slash, such that |
* pathname and append a trailing slash, such that |
* we can reliably check whether files are inside. |
* we can reliably check whether files are inside. |
*/ |
*/ |
if (NULL == realpath(targetdir, basedir)) { |
if (realpath(targetdir, basedir) == NULL) { |
if (report_baddir || errno != ENOENT) { |
if (report_baddir || errno != ENOENT) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "&%s: realpath", targetdir); |
say("", "&%s: realpath", targetdir); |
} |
} |
|
*basedir = '\0'; |
return 0; |
return 0; |
} else if (-1 == chdir(basedir)) { |
} else if (chdir(basedir) == -1) { |
if (report_baddir || errno != ENOENT) { |
if (report_baddir || errno != ENOENT) { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "&chdir"); |
say("", "&chdir"); |
} |
} |
|
*basedir = '\0'; |
return 0; |
return 0; |
} |
} |
chdir_status = 1; |
chdir_status = 1; |
cp = strchr(basedir, '\0'); |
basedir_len = strlen(basedir); |
if ('/' != cp[-1]) { |
if (basedir[basedir_len - 1] != '/') { |
if (cp - basedir >= PATH_MAX - 1) { |
if (basedir_len >= PATH_MAX - 1) { |
exitcode = (int)MANDOCLEVEL_SYSERR; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
say("", "Filename too long"); |
say("", "Filename too long"); |
|
*basedir = '\0'; |
|
basedir_len = 0; |
return 0; |
return 0; |
} |
} |
*cp++ = '/'; |
basedir[basedir_len++] = '/'; |
*cp = '\0'; |
basedir[basedir_len] = '\0'; |
} |
} |
return 1; |
return 1; |
} |
} |
|
|
|
#ifdef READ_ALLOWED_PATH |
|
static int |
|
read_allowed(const char *candidate) |
|
{ |
|
const char *cp; |
|
size_t len; |
|
|
|
for (cp = READ_ALLOWED_PATH;; cp += len) { |
|
while (*cp == ':') |
|
cp++; |
|
if (*cp == '\0') |
|
return 0; |
|
len = strcspn(cp, ":"); |
|
if (strncmp(candidate, cp, len) == 0) |
|
return 1; |
|
} |
|
} |
|
#endif |
|
|
static void |
static void |
say(const char *file, const char *format, ...) |
say(const char *file, const char *format, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
int use_errno; |
int use_errno; |
|
|
if ('\0' != *basedir) |
if (*basedir != '\0') |
fprintf(stderr, "%s", basedir); |
fprintf(stderr, "%s", basedir); |
if ('\0' != *basedir && '\0' != *file) |
if (*basedir != '\0' && *file != '\0') |
fputc('/', stderr); |
fputc('/', stderr); |
if ('\0' != *file) |
if (*file != '\0') |
fprintf(stderr, "%s", file); |
fprintf(stderr, "%s", file); |
|
|
use_errno = 1; |
use_errno = 1; |
if (NULL != format) { |
if (format != NULL) { |
switch (*format) { |
switch (*format) { |
case '&': |
case '&': |
format++; |
format++; |
Line 2351 say(const char *file, const char *format, ...) |
|
Line 2445 say(const char *file, const char *format, ...) |
|
break; |
break; |
} |
} |
} |
} |
if (NULL != format) { |
if (format != NULL) { |
if ('\0' != *basedir || '\0' != *file) |
if (*basedir != '\0' || *file != '\0') |
fputs(": ", stderr); |
fputs(": ", stderr); |
va_start(ap, format); |
va_start(ap, format); |
vfprintf(stderr, format, ap); |
vfprintf(stderr, format, ap); |
va_end(ap); |
va_end(ap); |
} |
} |
if (use_errno) { |
if (use_errno) { |
if ('\0' != *basedir || '\0' != *file || NULL != format) |
if (*basedir != '\0' || *file != '\0' || format != NULL) |
fputs(": ", stderr); |
fputs(": ", stderr); |
perror(NULL); |
perror(NULL); |
} else |
} else |