-/* $Id: mandocdb.c,v 1.141 2014/04/20 16:46:04 schwarze Exp $ */
+/* $Id: mandocdb.c,v 1.153 2014/06/21 16:18:25 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
static void dbprune(void);
static void filescan(const char *);
static void *hash_alloc(size_t, void *);
-static void hash_free(void *, size_t, void *);
-static void *hash_halloc(size_t, void *);
+static void hash_free(void *, void *);
+static void *hash_calloc(size_t, size_t, void *);
static void mlink_add(struct mlink *, const struct stat *);
static void mlink_check(struct mpage *, struct mlink *);
static void mlink_free(struct mlink *);
memset(&dirs, 0, sizeof(struct manpaths));
mpages_info.alloc = mlinks_info.alloc = hash_alloc;
- mpages_info.halloc = mlinks_info.halloc = hash_halloc;
- mpages_info.hfree = mlinks_info.hfree = hash_free;
+ mpages_info.calloc = mlinks_info.calloc = hash_calloc;
+ mpages_info.free = mlinks_info.free = hash_free;
mpages_info.key_offset = offsetof(struct mpage, inodev);
mlinks_info.key_offset = offsetof(struct mlink, file);
*/
#define CHECKOP(_op, _ch) do \
if (OP_DEFAULT != (_op)) { \
- fprintf(stderr, "-%c: Conflicting option\n", (_ch)); \
+ fprintf(stderr, "%s: -%c: Conflicting option\n", \
+ progname, (_ch)); \
goto usage; \
} while (/*CONSTCOND*/0)
break;
case 'T':
if (strcmp(optarg, "utf8")) {
- fprintf(stderr, "-T%s: Unsupported "
- "output format\n", optarg);
+ fprintf(stderr, "%s: -T%s: "
+ "Unsupported output format\n",
+ progname, optarg);
goto usage;
}
write_utf8 = 1;
argv += optind;
if (OP_CONFFILE == op && argc > 0) {
- fprintf(stderr, "-C: Too many arguments\n");
+ fprintf(stderr, "%s: -C: Too many arguments\n",
+ progname);
goto usage;
}
if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) {
/*
- * All of these deal with a specific directory.
+ * Most of these deal with a specific directory.
* Jump into that directory first.
*/
- if (0 == set_basedir(path_arg))
+ if (OP_TEST != op && 0 == set_basedir(path_arg))
goto out;
if (dbopen(1)) {
* manpath_parse() wants to do it.
*/
if (argc > 0) {
- dirs.paths = mandoc_calloc(argc,
- sizeof(char *));
+ dirs.paths = mandoc_reallocarray(NULL,
+ argc, sizeof(char *));
dirs.sz = (size_t)argc;
for (i = 0; i < argc; i++)
dirs.paths[i] = mandoc_strdup(argv[i]);
goto out;
if (0 == treescan())
goto out;
- if (0 == set_basedir(dirs.paths[j]))
- goto out;
if (0 == dbopen(0))
goto out;
mpages_merge(mc, mp);
- if (warnings &&
+ if (warnings && !nodb &&
! (MPARSE_QUICK & mparse_options))
names_check();
dbclose(0);
}
}
out:
- set_basedir(NULL);
manpath_free(&dirs);
mchars_free(mc);
mparse_free(mp);
fsec[-1] = '\0';
mlink = mandoc_calloc(1, sizeof(struct mlink));
- strlcpy(mlink->file, path, sizeof(mlink->file));
+ if (strlcpy(mlink->file, path,
+ sizeof(mlink->file)) >=
+ sizeof(mlink->file)) {
+ say(path, "Filename too long");
+ free(mlink);
+ continue;
+ }
mlink->dform = dform;
mlink->dsec = dsec;
mlink->arch = arch;
return;
}
- if (strstr(buf, basedir) == buf)
- start = buf + strlen(basedir) + 1;
- else if (OP_TEST == op)
+ if (OP_TEST == op)
start = buf;
+ else if (strstr(buf, basedir) == buf)
+ start = buf + strlen(basedir);
else {
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "%s: outside base directory", buf);
say(file, "&stat");
return;
}
- strlcpy(buf, file, sizeof(buf));
- start = strstr(buf, basedir) == buf ?
- buf + strlen(basedir) + 1 : buf;
+ if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) {
+ say(file, "Filename too long");
+ return;
+ }
+ start = buf;
+ if (OP_TEST != op && strstr(buf, basedir) == buf)
+ start += strlen(basedir);
}
mlink = mandoc_calloc(1, sizeof(struct mlink));
- strlcpy(mlink->file, start, sizeof(mlink->file));
+ if (strlcpy(mlink->file, start, sizeof(mlink->file)) >=
+ sizeof(mlink->file)) {
+ say(start, "Filename too long");
+ return;
+ }
/*
* First try to guess our directory structure.
mpage->form = FORM_NONE;
goto nextlink;
}
- if (strlcpy(buf, mlink->file, PATH_MAX) >= PATH_MAX) {
- if (warnings)
- say(mlink->file, "Filename too long");
- goto nextlink;
- }
+ (void)strlcpy(buf, mlink->file, sizeof(buf));
bufp = strstr(buf, "cat");
assert(NULL != bufp);
memcpy(bufp, "man", 3);
if (NULL != (bufp = strrchr(buf, '.')))
*++bufp = '\0';
- strlcat(buf, mlink->dsec, PATH_MAX);
+ (void)strlcat(buf, mlink->dsec, sizeof(buf));
if (NULL == ohash_find(&mlinks,
ohash_qlookup(&mlinks, buf)))
goto nextlink;
enum mandoclevel lvl;
str_info.alloc = hash_alloc;
- str_info.halloc = hash_halloc;
- str_info.hfree = hash_free;
+ str_info.calloc = hash_calloc;
+ str_info.free = hash_free;
str_info.key_offset = offsetof(struct str, key);
if (0 == nodb)
"AND mlinks.name == names.name"
")"
") JOIN ("
- "SELECT * FROM mlinks GROUP BY pageid"
+ "SELECT sec, arch, name, pageid FROM mlinks "
+ "GROUP BY pageid"
") USING (pageid);",
-1, &stmt, NULL);
s->mask |= v;
return;
} else if (NULL == s) {
- s = mandoc_calloc(sizeof(struct str) + sz + 1, 1);
+ s = mandoc_calloc(1, sizeof(struct str) + sz + 1);
memcpy(s->key, cp, sz);
ohash_insert(htab, slot, s);
}
mlink = mpage->mlinks;
if (nodb) {
+ for (key = ohash_first(&names, &slot); NULL != key;
+ key = ohash_next(&names, &slot)) {
+ if (key->rendered != key->key)
+ free(key->rendered);
+ free(key);
+ }
+ for (key = ohash_first(&strings, &slot); NULL != key;
+ key = ohash_next(&strings, &slot)) {
+ if (key->rendered != key->key)
+ free(key->rendered);
+ free(key);
+ }
+ if (0 == debug)
+ return;
while (NULL != mlink) {
fputs(mlink->name, stdout);
if (NULL == mlink->next ||
if (debug)
say(mlink->file, "Adding to database");
+ i = strlen(mpage->desc) + 1;
+ key = mandoc_calloc(1, sizeof(struct str) + i);
+ memcpy(key->key, mpage->desc, i);
+ render_key(mc, key);
+
i = 1;
- SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, mpage->desc);
+ SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, key->rendered);
SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, FORM_SRC == mpage->form);
SQL_STEP(stmts[STMT_INSERT_PAGE]);
mpage->pageid = sqlite3_last_insert_rowid(db);
sqlite3_reset(stmts[STMT_INSERT_PAGE]);
+ if (key->rendered != key->key)
+ free(key->rendered);
+ free(key);
+
while (NULL != mlink) {
dbadd_mlink(mlink);
mlink = mlink->next;
rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL);
if (SQLITE_OK != rc) {
exitcode = (int)MANDOCLEVEL_SYSERR;
- say(MANDOC_DB, "%s", sqlite3_errmsg(db));
+ if (SQLITE_CANTOPEN != rc)
+ say(MANDOC_DB, "%s", sqlite3_errstr(rc));
return(0);
}
goto prepare_statements;
goto create_tables;
if (MPARSE_QUICK & mparse_options) {
exitcode = (int)MANDOCLEVEL_SYSERR;
- say(MANDOC_DB "~", "%s", sqlite3_errmsg(db));
+ say(MANDOC_DB "~", "%s", sqlite3_errstr(rc));
return(0);
}
- if (strlcpy(tempfilename, "/tmp/mandocdb.XXXXXX",
- sizeof(tempfilename)) >= sizeof(tempfilename)) {
- exitcode = (int)MANDOCLEVEL_SYSERR;
- say("", "/tmp/mandocdb.XXXXXX: Filename too long");
- return(0);
- }
+ (void)strlcpy(tempfilename, "/tmp/mandocdb.XXXXXX",
+ sizeof(tempfilename));
if (NULL == mkdtemp(tempfilename)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&%s", tempfilename);
return(0);
}
- if (strlcat(tempfilename, "/" MANDOC_DB,
- sizeof(tempfilename)) >= sizeof(tempfilename)) {
- exitcode = (int)MANDOCLEVEL_SYSERR;
- say("", "%s/" MANDOC_DB ": Filename too long",
- tempfilename);
- return(0);
- }
+ (void)strlcat(tempfilename, "/" MANDOC_DB,
+ sizeof(tempfilename));
rc = sqlite3_open_v2(tempfilename, &db, ofl, NULL);
if (SQLITE_OK != rc) {
exitcode = (int)MANDOCLEVEL_SYSERR;
- say("", "%s: %s", tempfilename, sqlite3_errmsg(db));
+ say("", "%s: %s", tempfilename, sqlite3_errstr(rc));
return(0);
}
if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say(MANDOC_DB, "%s", sqlite3_errmsg(db));
+ sqlite3_close(db);
return(0);
}
prepare_statements:
- SQL_EXEC("PRAGMA foreign_keys = ON");
+ if (SQLITE_OK != sqlite3_exec(db,
+ "PRAGMA foreign_keys = ON", NULL, NULL, NULL)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say(MANDOC_DB, "PRAGMA foreign_keys: %s",
+ sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return(0);
+ }
+
sql = "DELETE FROM mpages WHERE pageid IN "
"(SELECT pageid FROM mlinks WHERE "
"sec=? AND arch=? AND name=?)";
* synchronous mode for much better performance.
*/
- if (real)
- SQL_EXEC("PRAGMA synchronous = OFF");
+ if (real && SQLITE_OK != sqlite3_exec(db,
+ "PRAGMA synchronous = OFF", NULL, NULL, NULL)) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say(MANDOC_DB, "PRAGMA synchronous: %s",
+ sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return(0);
+ }
#endif
return(1);
}
static void *
-hash_halloc(size_t sz, void *arg)
+hash_calloc(size_t n, size_t sz, void *arg)
{
- return(mandoc_calloc(sz, 1));
+ return(mandoc_calloc(n, sz));
}
static void *
}
static void
-hash_free(void *p, size_t sz, void *arg)
+hash_free(void *p, void *arg)
{
free(p);
set_basedir(const char *targetdir)
{
static char startdir[PATH_MAX];
- static int fd;
+ static int getcwd_status; /* 1 = ok, 2 = failure */
+ static int chdir_status; /* 1 = changed directory */
+ char *cp;
/*
- * Remember where we started by keeping a fd open to the origin
- * path component: throughout this utility, we chdir() a lot to
- * handle relative paths, and by doing this, we can return to
- * the starting point.
+ * Remember the original working directory, if possible.
+ * This will be needed if the second or a later directory
+ * on the command line is given as a relative path.
+ * Do not error out if the current directory is not
+ * searchable: Maybe it won't be needed after all.
*/
- if ('\0' == *startdir) {
- if (NULL == getcwd(startdir, PATH_MAX)) {
- exitcode = (int)MANDOCLEVEL_SYSERR;
- if (NULL != targetdir)
- say("", "&getcwd");
- return(0);
- }
- if (-1 == (fd = open(startdir, O_RDONLY, 0))) {
+ if (0 == getcwd_status) {
+ if (NULL == getcwd(startdir, sizeof(startdir))) {
+ getcwd_status = 2;
+ (void)strlcpy(startdir, strerror(errno),
+ sizeof(startdir));
+ } else
+ getcwd_status = 1;
+ }
+
+ /*
+ * We are leaving the old base directory.
+ * Do not use it any longer, not even for messages.
+ */
+ *basedir = '\0';
+
+ /*
+ * If and only if the directory was changed earlier and
+ * the next directory to process is given as a relative path,
+ * first go back, or bail out if that is impossible.
+ */
+ if (chdir_status && '/' != *targetdir) {
+ if (2 == getcwd_status) {
exitcode = (int)MANDOCLEVEL_SYSERR;
- say("", "&open %s", startdir);
+ say("", "getcwd: %s", startdir);
return(0);
}
- if (NULL == targetdir)
- targetdir = startdir;
- } else {
- if (-1 == fd)
- return(0);
- if (-1 == fchdir(fd)) {
- close(fd);
- basedir[0] = '\0';
+ if (-1 == chdir(startdir)) {
exitcode = (int)MANDOCLEVEL_SYSERR;
say("", "&chdir %s", startdir);
return(0);
}
- if (NULL == targetdir) {
- close(fd);
- return(1);
- }
}
+
+ /*
+ * Always resolve basedir to the canonicalized absolute
+ * pathname and append a trailing slash, such that
+ * we can reliably check whether files are inside.
+ */
if (NULL == realpath(targetdir, basedir)) {
- basedir[0] = '\0';
exitcode = (int)MANDOCLEVEL_BADARG;
say("", "&%s: realpath", targetdir);
return(0);
say("", "&chdir");
return(0);
}
+ chdir_status = 1;
+ cp = strchr(basedir, '\0');
+ if ('/' != cp[-1]) {
+ if (cp - basedir >= PATH_MAX - 1) {
+ exitcode = (int)MANDOCLEVEL_SYSERR;
+ say("", "Filename too long");
+ return(0);
+ }
+ *cp++ = '/';
+ *cp = '\0';
+ }
return(1);
}
if ('\0' != *basedir)
fprintf(stderr, "%s", basedir);
if ('\0' != *basedir && '\0' != *file)
- fputs("//", stderr);
+ fputc('/', stderr);
if ('\0' != *file)
fprintf(stderr, "%s", file);