version 1.146, 2014/04/27 23:08:56 |
version 1.156, 2014/08/10 23:54:41 |
|
|
* 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. |
*/ |
*/ |
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
#include "config.h" |
#endif |
|
|
|
#include <sys/stat.h> |
#include <sys/stat.h> |
|
#include <sys/types.h> |
#include <sys/wait.h> |
#include <sys/wait.h> |
|
|
#include <assert.h> |
#include <assert.h> |
Line 145 static int dbopen(int); |
|
Line 144 static int dbopen(int); |
|
static void dbprune(void); |
static void dbprune(void); |
static void filescan(const char *); |
static void filescan(const char *); |
static void *hash_alloc(size_t, void *); |
static void *hash_alloc(size_t, void *); |
static void hash_free(void *, size_t, void *); |
static void hash_free(void *, void *); |
static void *hash_halloc(size_t, void *); |
static void *hash_calloc(size_t, size_t, void *); |
static void mlink_add(struct mlink *, const struct stat *); |
static void mlink_add(struct mlink *, const struct stat *); |
static void mlink_check(struct mpage *, struct mlink *); |
static void mlink_check(struct mpage *, struct mlink *); |
static void mlink_free(struct mlink *); |
static void mlink_free(struct mlink *); |
Line 336 main(int argc, char *argv[]) |
|
Line 335 main(int argc, char *argv[]) |
|
memset(&dirs, 0, sizeof(struct manpaths)); |
memset(&dirs, 0, sizeof(struct manpaths)); |
|
|
mpages_info.alloc = mlinks_info.alloc = hash_alloc; |
mpages_info.alloc = mlinks_info.alloc = hash_alloc; |
mpages_info.halloc = mlinks_info.halloc = hash_halloc; |
mpages_info.calloc = mlinks_info.calloc = hash_calloc; |
mpages_info.hfree = mlinks_info.hfree = hash_free; |
mpages_info.free = mlinks_info.free = hash_free; |
|
|
mpages_info.key_offset = offsetof(struct mpage, inodev); |
mpages_info.key_offset = offsetof(struct mpage, inodev); |
mlinks_info.key_offset = offsetof(struct mlink, file); |
mlinks_info.key_offset = offsetof(struct mlink, file); |
Line 355 main(int argc, char *argv[]) |
|
Line 354 main(int argc, char *argv[]) |
|
*/ |
*/ |
#define CHECKOP(_op, _ch) do \ |
#define CHECKOP(_op, _ch) do \ |
if (OP_DEFAULT != (_op)) { \ |
if (OP_DEFAULT != (_op)) { \ |
fprintf(stderr, "-%c: Conflicting option\n", (_ch)); \ |
fprintf(stderr, "%s: -%c: Conflicting option\n", \ |
|
progname, (_ch)); \ |
goto usage; \ |
goto usage; \ |
} while (/*CONSTCOND*/0) |
} while (/*CONSTCOND*/0) |
|
|
Line 391 main(int argc, char *argv[]) |
|
Line 391 main(int argc, char *argv[]) |
|
break; |
break; |
case 'T': |
case 'T': |
if (strcmp(optarg, "utf8")) { |
if (strcmp(optarg, "utf8")) { |
fprintf(stderr, "-T%s: Unsupported " |
fprintf(stderr, "%s: -T%s: " |
"output format\n", optarg); |
"Unsupported output format\n", |
|
progname, optarg); |
goto usage; |
goto usage; |
} |
} |
write_utf8 = 1; |
write_utf8 = 1; |
Line 419 main(int argc, char *argv[]) |
|
Line 420 main(int argc, char *argv[]) |
|
argv += optind; |
argv += optind; |
|
|
if (OP_CONFFILE == op && argc > 0) { |
if (OP_CONFFILE == op && argc > 0) { |
fprintf(stderr, "-C: Too many arguments\n"); |
fprintf(stderr, "%s: -C: Too many arguments\n", |
|
progname); |
goto usage; |
goto usage; |
} |
} |
|
|
Line 433 main(int argc, char *argv[]) |
|
Line 435 main(int argc, char *argv[]) |
|
if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { |
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. |
* Jump into that directory first. |
*/ |
*/ |
if (0 == set_basedir(path_arg)) |
if (OP_TEST != op && 0 == set_basedir(path_arg)) |
goto out; |
goto out; |
|
|
if (dbopen(1)) { |
if (dbopen(1)) { |
Line 506 main(int argc, char *argv[]) |
|
Line 508 main(int argc, char *argv[]) |
|
goto out; |
goto out; |
if (0 == treescan()) |
if (0 == treescan()) |
goto out; |
goto out; |
if (0 == set_basedir(dirs.paths[j])) |
|
goto out; |
|
if (0 == dbopen(0)) |
if (0 == dbopen(0)) |
goto out; |
goto out; |
|
|
mpages_merge(mc, mp); |
mpages_merge(mc, mp); |
if (warnings && |
if (warnings && !nodb && |
! (MPARSE_QUICK & mparse_options)) |
! (MPARSE_QUICK & mparse_options)) |
names_check(); |
names_check(); |
dbclose(0); |
dbclose(0); |
Line 525 main(int argc, char *argv[]) |
|
Line 525 main(int argc, char *argv[]) |
|
} |
} |
} |
} |
out: |
out: |
set_basedir(NULL); |
|
manpath_free(&dirs); |
manpath_free(&dirs); |
mchars_free(mc); |
mchars_free(mc); |
mparse_free(mp); |
mparse_free(mp); |
Line 800 filescan(const char *file) |
|
Line 799 filescan(const char *file) |
|
return; |
return; |
} |
} |
|
|
if (strstr(buf, basedir) == buf) |
if (OP_TEST == op) |
start = buf + strlen(basedir) + 1; |
|
else if (OP_TEST == op) |
|
start = buf; |
start = buf; |
|
else if (strstr(buf, basedir) == buf) |
|
start = buf + strlen(basedir); |
else { |
else { |
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "%s: outside base directory", buf); |
say("", "%s: outside base directory", buf); |
Line 829 filescan(const char *file) |
|
Line 828 filescan(const char *file) |
|
say(file, "Filename too long"); |
say(file, "Filename too long"); |
return; |
return; |
} |
} |
start = strstr(buf, basedir) == buf ? |
start = buf; |
buf + strlen(basedir) + 1 : buf; |
if (OP_TEST != op && strstr(buf, basedir) == buf) |
|
start += strlen(basedir); |
} |
} |
|
|
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
mlink = mandoc_calloc(1, sizeof(struct mlink)); |
Line 1090 mpages_merge(struct mchars *mc, struct mparse *mp) |
|
Line 1090 mpages_merge(struct mchars *mc, struct mparse *mp) |
|
enum mandoclevel lvl; |
enum mandoclevel lvl; |
|
|
str_info.alloc = hash_alloc; |
str_info.alloc = hash_alloc; |
str_info.halloc = hash_halloc; |
str_info.calloc = hash_calloc; |
str_info.hfree = hash_free; |
str_info.free = hash_free; |
str_info.key_offset = offsetof(struct str, key); |
str_info.key_offset = offsetof(struct str, key); |
|
|
if (0 == nodb) |
if (0 == nodb) |
Line 1197 mpages_merge(struct mchars *mc, struct mparse *mp) |
|
Line 1197 mpages_merge(struct mchars *mc, struct mparse *mp) |
|
goto nextpage; |
goto nextpage; |
} else if (NULL != mdoc) { |
} else if (NULL != mdoc) { |
mpage->form = FORM_SRC; |
mpage->form = FORM_SRC; |
mpage->sec = |
mpage->sec = mdoc_meta(mdoc)->msec; |
mandoc_strdup(mdoc_meta(mdoc)->msec); |
mpage->sec = mandoc_strdup( |
|
NULL == mpage->sec ? "" : mpage->sec); |
mpage->arch = mdoc_meta(mdoc)->arch; |
mpage->arch = mdoc_meta(mdoc)->arch; |
mpage->arch = mandoc_strdup( |
mpage->arch = mandoc_strdup( |
NULL == mpage->arch ? "" : mpage->arch); |
NULL == mpage->arch ? "" : mpage->arch); |
Line 1305 names_check(void) |
|
Line 1306 names_check(void) |
|
say("", "%s", sqlite3_errmsg(db)); |
say("", "%s", sqlite3_errmsg(db)); |
|
|
while (SQLITE_ROW == (irc = sqlite3_step(stmt))) { |
while (SQLITE_ROW == (irc = sqlite3_step(stmt))) { |
name = sqlite3_column_text(stmt, 0); |
name = (const char *)sqlite3_column_text(stmt, 0); |
sec = sqlite3_column_text(stmt, 1); |
sec = (const char *)sqlite3_column_text(stmt, 1); |
arch = sqlite3_column_text(stmt, 2); |
arch = (const char *)sqlite3_column_text(stmt, 2); |
key = sqlite3_column_text(stmt, 3); |
key = (const char *)sqlite3_column_text(stmt, 3); |
say("", "%s(%s%s%s) lacks mlink \"%s\"", name, sec, |
say("", "%s(%s%s%s) lacks mlink \"%s\"", name, sec, |
'\0' == *arch ? "" : "/", |
'\0' == *arch ? "" : "/", |
'\0' == *arch ? "" : arch, key); |
'\0' == *arch ? "" : arch, key); |
Line 1996 dbadd(struct mpage *mpage, struct mchars *mc) |
|
Line 1997 dbadd(struct mpage *mpage, struct mchars *mc) |
|
mlink = mpage->mlinks; |
mlink = mpage->mlinks; |
|
|
if (nodb) { |
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) |
if (0 == debug) |
return; |
return; |
while (NULL != mlink) { |
while (NULL != mlink) { |
Line 2024 dbadd(struct mpage *mpage, struct mchars *mc) |
|
Line 2037 dbadd(struct mpage *mpage, struct mchars *mc) |
|
if (debug) |
if (debug) |
say(mlink->file, "Adding to database"); |
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; |
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_BIND_INT(stmts[STMT_INSERT_PAGE], i, FORM_SRC == mpage->form); |
SQL_STEP(stmts[STMT_INSERT_PAGE]); |
SQL_STEP(stmts[STMT_INSERT_PAGE]); |
mpage->pageid = sqlite3_last_insert_rowid(db); |
mpage->pageid = sqlite3_last_insert_rowid(db); |
sqlite3_reset(stmts[STMT_INSERT_PAGE]); |
sqlite3_reset(stmts[STMT_INSERT_PAGE]); |
|
|
|
if (key->rendered != key->key) |
|
free(key->rendered); |
|
free(key); |
|
|
while (NULL != mlink) { |
while (NULL != mlink) { |
dbadd_mlink(mlink); |
dbadd_mlink(mlink); |
mlink = mlink->next; |
mlink = mlink->next; |
Line 2209 dbopen(int real) |
|
Line 2231 dbopen(int real) |
|
rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL); |
rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL); |
if (SQLITE_OK != rc) { |
if (SQLITE_OK != rc) { |
exitcode = (int)MANDOCLEVEL_SYSERR; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
say(MANDOC_DB, "%s", sqlite3_errstr(rc)); |
if (SQLITE_CANTOPEN != rc) |
|
say(MANDOC_DB, "%s", sqlite3_errstr(rc)); |
return(0); |
return(0); |
} |
} |
goto prepare_statements; |
goto prepare_statements; |
Line 2328 prepare_statements: |
|
Line 2351 prepare_statements: |
|
} |
} |
|
|
static void * |
static void * |
hash_halloc(size_t sz, void *arg) |
hash_calloc(size_t n, size_t sz, void *arg) |
{ |
{ |
|
|
return(mandoc_calloc(1, sz)); |
return(mandoc_calloc(n, sz)); |
} |
} |
|
|
static void * |
static void * |
Line 2342 hash_alloc(size_t sz, void *arg) |
|
Line 2365 hash_alloc(size_t sz, void *arg) |
|
} |
} |
|
|
static void |
static void |
hash_free(void *p, size_t sz, void *arg) |
hash_free(void *p, void *arg) |
{ |
{ |
|
|
free(p); |
free(p); |
|
|
set_basedir(const char *targetdir) |
set_basedir(const char *targetdir) |
{ |
{ |
static char startdir[PATH_MAX]; |
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 |
* Remember the original working directory, if possible. |
* path component: throughout this utility, we chdir() a lot to |
* This will be needed if the second or a later directory |
* handle relative paths, and by doing this, we can return to |
* on the command line is given as a relative path. |
* the starting point. |
* Do not error out if the current directory is not |
|
* searchable: Maybe it won't be needed after all. |
*/ |
*/ |
if ('\0' == *startdir) { |
if (0 == getcwd_status) { |
if (NULL == getcwd(startdir, PATH_MAX)) { |
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; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
if (NULL != targetdir) |
say("", "getcwd: %s", startdir); |
say("", "&getcwd"); |
|
return(0); |
return(0); |
} |
} |
if (-1 == (fd = open(startdir, O_RDONLY, 0))) { |
if (-1 == chdir(startdir)) { |
exitcode = (int)MANDOCLEVEL_SYSERR; |
exitcode = (int)MANDOCLEVEL_SYSERR; |
say("", "&open %s", startdir); |
|
return(0); |
|
} |
|
if (NULL == targetdir) |
|
targetdir = startdir; |
|
} else { |
|
if (-1 == fd) |
|
return(0); |
|
if (-1 == fchdir(fd)) { |
|
close(fd); |
|
basedir[0] = '\0'; |
|
exitcode = (int)MANDOCLEVEL_SYSERR; |
|
say("", "&chdir %s", startdir); |
say("", "&chdir %s", startdir); |
return(0); |
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)) { |
if (NULL == realpath(targetdir, basedir)) { |
basedir[0] = '\0'; |
|
exitcode = (int)MANDOCLEVEL_BADARG; |
exitcode = (int)MANDOCLEVEL_BADARG; |
say("", "&%s: realpath", targetdir); |
say("", "&%s: realpath", targetdir); |
return(0); |
return(0); |
Line 2399 set_basedir(const char *targetdir) |
|
Line 2433 set_basedir(const char *targetdir) |
|
say("", "&chdir"); |
say("", "&chdir"); |
return(0); |
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); |
return(1); |
} |
} |
|
|
Line 2411 say(const char *file, const char *format, ...) |
|
Line 2456 say(const char *file, const char *format, ...) |
|
if ('\0' != *basedir) |
if ('\0' != *basedir) |
fprintf(stderr, "%s", basedir); |
fprintf(stderr, "%s", basedir); |
if ('\0' != *basedir && '\0' != *file) |
if ('\0' != *basedir && '\0' != *file) |
fputs("//", stderr); |
fputc('/', stderr); |
if ('\0' != *file) |
if ('\0' != *file) |
fprintf(stderr, "%s", file); |
fprintf(stderr, "%s", file); |
|
|