#include <inttypes.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/param.h>
#include <libxml/HTMLparser.h>
#include <libxml/HTMLtree.h>
#include <libxml/tree.h>
#include <libxml/parserInternals.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <libgen.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#ifdef ENABLE_CORE_DUMPS
#include <sys/resource.h>
#endif
#ifdef USE_STRCOMPAT
#include "strcompat.h"
#endif
#ifndef C99
#define FMT_SIZE_T "%zu"
#else
#define FMT_SIZE_T "%lu"
#endif
#define DEPTH_TYPE int
#define DEPTH_TYPE_PRINTF "%d"
#define PART_REF 1
#define PART_LANG 2
#define PART_REST 4
#define PART_ALL 7
#include <stdarg.h>
int safe_asprintf(char **ret, const char *format, ...) __attribute__((format (printf, 2, 3)));
#undef DEBUG_NAMEFROMAPPLEREF
#undef NEAR_MATCH_DEBUG // 1 2
#define MAXTHREADS 16
#define ERRS stderr
#define MAXSEEDFILES 1024
#define MAXEXTREFS 1024
#undef NOAVL
#undef TESTING_AVL
#ifdef TESTING_AVL
int testing_avl = 1;
#else
int testing_avl = 0;
#endif
char *realpath_workaround(char *path, char *buffer)
{
if (!buffer) {
if (!strcmp(path, "/")) {
return strdup(path);
}
}
return realpath(path, buffer);
}
#define realpath(a, b) realpath_workaround(a, b)
typedef struct refparts {
char *refpart;
char *langpart;
char *rest;
} *refparts_t;
#define RP_CACHE
typedef struct _xrefnode {
char *basepath;
char *filename;
#ifdef RP_CACHE
char *filename_rp;
#endif
char *fullpath; char *xref;
char *title;
int fromseed : 1;
int force_absolute : 1;
DEPTH_TYPE maxleftdepth, maxrightdepth;
struct _xrefnode *left, *right, *dup, *parent;
} *xrefnode_t;
typedef struct _xrefbyname
{
char *name;
xrefnode_t node;
DEPTH_TYPE maxleftdepth, maxrightdepth;
struct _xrefbyname *left, *right, *dup, *parent;
} *xrefbyname_t;
struct nodelistitem {
xmlNode *node;
struct nodelistitem *next;
struct nodelistitem *prev;
};
typedef struct fileref {
char name[MAXPATHLEN];
struct fileref *next;
struct fileref *threadnext;
} *fileref_t;
typedef struct searchobj {
char *uri;
xrefnode_t obj;
} *searchobj_t;
void checkDoc(xmlNode *node, xmlNode *parent, xmlNode *prev, htmlDocPtr dp);
int ispartialmatch(char *partial_ref, char *complete_ref);
struct nodelistitem *nodelist(char *name, xmlNode * root);
void openlogfile();
void closelogfile();
int isURI(char *filename);
int insertName(xrefbyname_t node, xrefbyname_t tree);
char *xmlNodeGetRawString(htmlDocPtr dp, xmlNode * node, int whatever);
xmlNode *xmlNodeForComment(xmlChar *commentguts, htmlDocPtr dp);
char *resolve(char *xref, char *filename, int *retarget, char **frametgt);
static void *resolve_main(void *ref);
void setup_redirection(void);
char *nameFromAppleRef(char *ref);
xrefnode_t refByName(char *name, char *basepath);
void redirect_stderr_to_null(void);
void restore_stderr(void);
void writeXRefFile(char *filename, char *indir);
int readXRefFile(char *filename, char *basepath, int force_absolute);
void writeFile(xmlNode * node, htmlDocPtr dp, char *filename);
void gatherXRefs(xmlNode * node, htmlDocPtr dp, char *filename);
void resolveLinks(xmlNode * node, htmlDocPtr dp, char *filename, char *filename_rp);
char *proptext(char *name, struct _xmlAttr *prop);
void parseUsage(xmlNode * node);
int isCommentedAnchor(char *commentString);
void addAttribute(xmlNode * node, char *attname, char *attstring);
char *propString(xmlNode * node);
fileref_t getFiles(char *curPath);
void print_statistics(void);
int resolve_mainsub(int pos);
int countfiles(fileref_t rethead);
int onSameLevel(xmlNode * a, xmlNode * b);
int tailcompare(char *string, char *tail);
void writeFile_sub(xmlNode * node, htmlDocPtr dp, FILE * fp,
int this_node_and_children_only);
void addXRefSub(xrefnode_t newnode, xrefnode_t tree);
char *relpath(char *origPath, char *filename, int isDir);
char *fixpath(char *name);
int has_target(xmlNode * node);
char *ts_basename(char *path);
char *ts_dirname(char *path);
int ishdindex(char *filename);
void printusage();
int printNodeRange(xmlNode * start, xmlNode * end);
int test_xrefnode_t_tree();
int test_xrefbyname_t_tree();
void *db_malloc(size_t length);
void db_free(void *ptr);
void searchfree(searchobj_t obj);
void rebalance_xrefnode_t_tree(xrefnode_t node, xrefnode_t fromnode);
void rebalance_xrefbyname_t_tree(xrefbyname_t node, xrefbyname_t fromnode);
DEPTH_TYPE verify_xrefnode_t_tree(xrefnode_t node);
DEPTH_TYPE verify_xrefbyname_t_tree(xrefbyname_t node);
void free_xrefnode_t_tree(xrefnode_t node);
void free_xrefbyname_t_tree(xrefbyname_t node);
char *nodefilenamerealpath(xrefnode_t node);
#ifdef ENABLE_CORE_DUMPS
static int EnableCoreDumps(void);
#endif
int nopercent = 0;
xrefnode_t nodehead = NULL;
xrefbyname_t namehead = NULL;
int debugging = 0;
int filedebug = 0;
int writedebug = 0;
int debug_relpath = 0;
int warn_each = 0;
int debug_reparent = 0;
int quick_test = 0;
int nowrite = 0;
int stderrfd = -1;
int nullfd = -1;
FILE *logfile = NULL;
char *logname = NULL;
int resolved = 0;
int unresolved = 0;
int unresolved_explicit = 0;
int nfiles = 0;
int broken = 0;
int plain = 0;
int thread_exit[MAXTHREADS];
int thread_processed_files[MAXTHREADS];
int nthreads = 2;
int duplicates = 0;
int seeding_in_progress = 0;
int nextrefs = 0;
char *extrefs[MAXEXTREFS];
fileref_t threadfiles[MAXTHREADS];
char *global_installedpath = NULL;
char *global_basepath = "/";
int global_basepath_set = 0;
char *progname;
char *inputDirectory = NULL;
int force_absolute_globally = 0;
int nodot = 0;
int global_option_disable_partial_ref_matching = 0;
int global_option_disable_name_matching = 0;
int main(int argc, char *argv[])
{
htmlDocPtr dp;
xmlNode *root;
int cumulative_exit = 0;
char *filename;
char *cwd;
fileref_t files, curfile;
char *xref_output_file = NULL;
char *seedfiles[MAXSEEDFILES];
char *seedfilebase[MAXSEEDFILES];
int seedfileabsolute[MAXSEEDFILES];
#ifdef ENABLE_CORE_DUMPS
EnableCoreDumps();
#endif
bzero(seedfiles, (sizeof(seedfiles[0]) * MAXSEEDFILES));
bzero(seedfilebase, (sizeof(seedfilebase[0]) * MAXSEEDFILES));
bzero(seedfileabsolute, (sizeof(seedfileabsolute[0]) * MAXSEEDFILES));
int nseedfiles = 0;
test_xrefnode_t_tree();
test_xrefbyname_t_tree();
#if TESTING_AVL > 1
sleep(20);
exit(-1);
#endif
if (0) {
printf("%d\n", ishdindex("/Users/dg/XManL/manpages/index.html"));
printf("%d\n",
ishdindex
("/Users/dg/headerdoc-techpubs/framework_output/force_feedback/ForceFeedback_h/index.html"));
printf("%d\n",
ishdindex
("/Users/dg/headerdoc-techpubs/ExampleHeaders/enumsTest/index.html"));
exit(0);
}
setup_redirection();
if (argc < 1) {
fprintf(ERRS, "resolveLinks: No arguments given.\n");
printusage();
exit(-1);
}
if (0) {
char *test;
printf("MALLOCTEST\n");
test = malloc(37 * sizeof(char));
if (!test) {
fprintf(stderr, "Out of memory in test code\n");
exit(-1);
}
free(test);
printf("MALLOCTEST DONE\n");
sleep(5);
}
#ifdef LIBXML_TEST_VERSION
LIBXML_TEST_VERSION;
#endif
if (argc < 2) {
printusage();
exit(-1);
}
progname = argv[0];
{
int temp, debug_flags;
while ((temp = getopt(argc, argv, "ab:d:Dhi:nNpPr:s:S:t:Tx:"))) {
if (temp == -1)
break;
switch (temp) {
case 'a':
if (!nseedfiles) {
force_absolute_globally = 1;
} else {
seedfileabsolute[nseedfiles-1] = 1;
}
break;
case 'b':
global_basepath = realpath(optarg, NULL); if (!global_basepath) {
fprintf(stderr, "Could not set global_basepath to %s\n", optarg);
perror("resolveLinks");
exit(-1);
}
if (optarg[strlen(optarg)-1] == '/' && strcmp(global_basepath, "/")) {
char *temp = global_basepath;
safe_asprintf(&global_basepath, "%s/", temp); if (!global_basepath) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(temp);
}
global_basepath_set = 1;
printf("Set global base path to %s\n", global_basepath);
break;
case 'i':
global_installedpath = optarg;
break;
case 'r':
if (nextrefs > MAXEXTREFS) {
fprintf(ERRS,
"Maximum number of external reference anchor types (%d) exceeded. Extra files ignored.\n",
MAXSEEDFILES);
} else {
extrefs[nextrefs++] = optarg;
}
break;
case 's':
if (nseedfiles > MAXSEEDFILES) {
fprintf(ERRS,
"Maximum number of seed files (%d) exceeded. Extra files ignored.\n",
MAXSEEDFILES);
} else {
seedfiles[nseedfiles++] = optarg;
}
break;
case 'S':
if (!nseedfiles) {
fprintf(ERRS,
"Error: the -S flag must follow a -s flag.\n");
exit(-1);
}
if (optarg[0] != '/') {
char *temp = realpath(optarg, NULL);
if (!temp) {
fprintf(ERRS,
"Error: Seed file base path \"%s\" is not an absolute path and\n"
"does not exist relative to the current directory.\n",
optarg);
exit(-1);
}
seedfilebase[nseedfiles-1] = temp;
} else {
seedfilebase[nseedfiles-1] = optarg;
}
break;
case 'x':
xref_output_file = optarg;
break;
case 'T':
testing_avl = 1;
break;
case 't':
nthreads = atoi(optarg);
break;
case 'd':
debug_flags = atoi(optarg);
debugging = ((debug_flags & 1) != 0);
filedebug = ((debug_flags & 2) != 0);
writedebug = ((debug_flags & 4) != 0);
debug_relpath = ((debug_flags & 8) != 0);
debug_reparent = ((debug_flags & 16) != 0);
warn_each = ((debug_flags & 32) != 0);
nthreads = 0;
break;
case 'D':
nodot = 1;
break;
case 'n':
nowrite = 1;
break;
case 'N':
global_option_disable_name_matching = 1;
break;
case 'P':
global_option_disable_partial_ref_matching = 1;
break;
case 'p':
nopercent = 1;
break;
case 'h':
default:
printusage();
exit(-1);
}
}
inputDirectory = argv[optind];
cwd = getcwd(NULL, 0);
if (chdir(inputDirectory)) {
perror(inputDirectory);
printusage();
exit(-1);
}
if (chdir(cwd)) {
perror(inputDirectory);
printusage();
exit(-1);
}
inputDirectory = realpath(argv[optind], NULL); if (!inputDirectory) {
fprintf(stderr, "Could not open input directory.\n");
perror(inputDirectory);
printusage();
exit(-1);
}
if (argv[optind][strlen(argv[optind])-1] == '/') {
char *temp = inputDirectory;
safe_asprintf(&inputDirectory, "%s/", temp); if (!inputDirectory) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(temp);
}
}
if (debug_reparent)
nthreads = 0;
if (nseedfiles) {
int i;
seeding_in_progress = 1;
printf("Loading seed files.\n");
for (i = 0; i < nseedfiles; i++) {
int ret = readXRefFile(seedfiles[i], seedfilebase[i] ? seedfilebase[i] : "/", seedfileabsolute[i]);
if (!ret) {
fprintf(ERRS, "%s: xref seed file %s missing or malformed.\n",
progname, seedfiles[i]);
}
}
seeding_in_progress = 0;
}
duplicates = 0;
{
if (!chdir(inputDirectory)) {
char *newdir = getcwd(NULL, 0);
char *allyourbase = ts_basename(newdir);
char *ayb_fp = NULL;
free(newdir);
printf("Finding files.\n");
ayb_fp = fixpath(allyourbase);
if (!((files = getFiles(ayb_fp)))) {
fprintf(ERRS, "No HTML files found.\n");
exit(-1);
}
if (chdir("..")) {
char *tempcwd = getcwd(NULL, MAXPATHLEN);
fprintf(stderr, "Could not change to directory \"..\" from \"%s\"\n", tempcwd);
perror("resolveLinks");
free(tempcwd);
exit(-1);
}
free(allyourbase);
} else {
fprintf(stderr, "Could not change into directory \"%s\"\n", inputDirectory);
perror(inputDirectory);
exit(-1);
}
}
nfiles = countfiles(files);
if (debugging || filedebug) {
for (curfile = files; curfile; curfile = curfile->next) {
printf("\nWill check %s", curfile->name);
}
}
printf("\nChecking for cross-references\n");
if (nthreads >= 0) {
for (curfile = files; curfile; curfile = curfile->next) {
filename = curfile->name;
if (!nodot) printf(".");
fflush(stdout);
if (debugging) {
printf("FILE: %s\n", filename);
fflush(stdout);
}
redirect_stderr_to_null();
if (!(dp = htmlParseFile(filename, ""))) {
restore_stderr();
fprintf(ERRS,
"error: resolveLinks: could not parse HTML file %s\n",
filename);
fprintf(ERRS, "CWD is %s\n", getcwd(NULL, 0));
exit(-1);
}
root = xmlDocGetRootElement(dp);
restore_stderr();
if (quick_test < 2)
gatherXRefs(root, dp, filename);
xmlFreeDoc(dp);
}
fprintf(stderr, "\nWriting xref file\n");
writeXRefFile(xref_output_file, cwd);
verify_xrefnode_t_tree(nodehead);
verify_xrefbyname_t_tree(namehead);
#ifdef TESTING_AVL
sleep(20);
exit(-1);
#endif
} else {
quick_test = 1;
}
if (nowrite == 1)
exit(0);
#ifdef OLD_CODE
{
fileref_t next = files;
int count = 0;
for (count = 0; count < nthreads; count++) {
threadfiles[count % nthreads] = NULL;
}
for (curfile = files; next; curfile = next, count++) {
next = curfile->next;
curfile->next = threadfiles[count % nthreads];
threadfiles[count % nthreads] = curfile;
}
for (count = 0; count < nthreads; count++) {
if (debugging)
printf("FILES[%d] = %d\n", count,
countfiles(threadfiles[count]));
}
}
#endif
if (nthreads > 0) {
printf("\nResolving links (multithreaded)\n");
pthread_t threads[MAXTHREADS];
int thread_exists[MAXTHREADS];
int i;
bzero(&thread_exists, sizeof(thread_exists));
for (i = 0; i < nthreads; i++) {
uintptr_t temp_i = i;
pthread_attr_t *attr = NULL;
thread_exists[i] = 1;
if (pthread_create
(&threads[i], attr, resolve_main, (void *) temp_i)) {
printf("Thread %d failed. Running in main thread.\n", i);
resolve_mainsub(i);
thread_exists[i] = 0;
}
}
for (i = 0; i < nthreads; i++) {
int joinret;
if (debugging)
printf("JOINING %d\n", i);
if (thread_exists[i]) {
if ((joinret = pthread_join(threads[i], NULL))) {
if (debugging)
printf("JOIN RETURNED %d\n", joinret);
}
}
cumulative_exit =
thread_exit[i] ? thread_exit[i] : cumulative_exit;
if (debugging)
printf("thread_exit[%d] = %d\n", i, thread_exit[i]);
}
} else {
if (!nthreads)
printf("\nResolving links (single threaded)\n");
cumulative_exit = resolve_mainsub(0);
}
printf("\nDone\n");
print_statistics();
xmlCleanupParser();
if (cumulative_exit) {
printf("Exiting with status %d\n", cumulative_exit);
}
#ifndef TEST_DELAY
if (testing_avl)
#endif
sleep(20);
exit(cumulative_exit);
}
void refpartsfree(refparts_t rp)
{
if (rp->refpart) free(rp->refpart);
if (rp->langpart) free(rp->langpart);
if (rp->rest) free(rp->rest);
free(rp);
}
static void *resolve_main(void *ref)
{
int ret;
if (debugging)
printf("Thread %d spawned.\n", (int) (uintptr_t) ref);
ret = resolve_mainsub((int) (uintptr_t) ref);
thread_exit[(int) (uintptr_t) ref] = ret;
pthread_exit(NULL);
}
#define OLD_LIBXML
int resolve_mainsub(int pos)
{
int localDebug = 0;
fileref_t files, curfile;
char *filename;
htmlDocPtr dp;
xmlNode *root;
char tempname[MAXNAMLEN];
#ifndef OLD_LIBXML
htmlParserCtxtPtr ctxt;
int options =
HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET;
#endif
files = threadfiles[pos];
thread_processed_files[pos] = 0;
#ifndef OLD_LIBXML
int ks = 1;
#endif
for (curfile = files; curfile; curfile = curfile->threadnext) {
thread_processed_files[pos]++;
filename = curfile->name;
if (debugging || writedebug || debug_reparent || localDebug)
printf("READING FILE: %s\n", filename);
snprintf(tempname, MAXNAMLEN, "%s-temp%d-%d", filename, getpid(),
pos);
redirect_stderr_to_null();
#ifdef OLD_LIBXML
if (!(dp = htmlParseFile(filename, NULL)))
#else
ctxt = htmlCreateFileParserCtxt(filename, NULL);
if (!ctxt) {
fprintf(ERRS, "error: could not create context\n");
exit(-1);
}
ctxt->options = options;
ctxt->space = &ks;
ctxt->sax->ignorableWhitespace = NULL;
ctxt->keepBlanks = 1;
htmlParseDocument(ctxt);
dp = ctxt->myDoc;
if (!dp)
#endif
{
restore_stderr();
fprintf(ERRS,
"error: resolveLinks: could not parse HTML file %s in resolve_mainsub().\n",
curfile->name);
return -1;
}
root = xmlDocGetRootElement(dp);
restore_stderr();
if (!quick_test) {
char *filename_rp = realpath(filename, NULL);
resolveLinks(root, dp, filename, filename_rp);
free(filename_rp);
if (!nodot) printf(".");
fflush(stdout);
} else {
printf("X");
fflush(stdout);
}
if (debugging || writedebug || debug_reparent)
printf("WRITING FILE: %s\n", filename);
writeFile(root, dp, tempname);
xmlFreeDoc(dp);
if (nowrite == 2) {
fprintf(stderr, "TEMPNAME: %s\n", tempname);
} else if (rename(tempname, filename)) {
fprintf(ERRS, "error: error renaming temp file over original.\n");
perror("resolveLinks");
return -1;
}
#ifndef OLD_LIBXML
htmlFreeParserCtxt(ctxt);
#endif
}
return 0;
}
char *textmatching(char *name, xmlNode * cur, int missing_ok, int recurse);
xmlNode *nodematching(char *name, xmlNode * cur, int recurse);
int addXRefFromLine(char *line, char *basepath, int force_absolute)
{
char *iter = line;
char *xref = NULL;
char *title = NULL;
xrefnode_t newnode;
if (!strcmp(line, "Cross-references seen (for debugging only)"))
return -1;
if (!strlen(line))
return -1;
while (*iter && *iter != 1) {
iter++;
}
if (*iter) {
*iter = '\0';
iter++;
xref = iter;
}
while (*iter && *iter != 1) {
iter++;
}
if (*iter) {
*iter = '\0';
iter++;
title = iter;
}
if (!xref || !title) {
fprintf(ERRS, "warning: Corrupted line in xref file.\n");
return 0;
}
if (!((newnode = malloc(sizeof(*newnode))))) { fprintf(ERRS, "error: Out of memory reading xref file.\n");
exit(-1);
}
newnode->filename = strdup(line);
#ifdef RP_CACHE
newnode->filename_rp = NULL;
#endif
newnode->basepath = strdup(basepath);
newnode->fullpath = NULL;
newnode->xref = strdup(xref);
newnode->title = strdup(title);
newnode->left = NULL;
newnode->right = NULL;
newnode->parent = NULL;
newnode->maxleftdepth = 0;
newnode->maxrightdepth = 0;
newnode->dup = NULL;
newnode->fromseed = seeding_in_progress;
newnode->force_absolute = force_absolute;
if (debugging) {
printf("Added xref: uid=%s, filename_or_uri=%s, title=%s", newnode->xref, newnode->filename, newnode->title);
}
xrefbyname_t newname = malloc(sizeof(*newname)); newname->name = nameFromAppleRef(newnode->xref);
newname->node = newnode;
newname->left = NULL;
newname->right = NULL;
newname->dup = NULL;
newname->parent = NULL;
newname->maxleftdepth = 0;
newname->maxrightdepth = 0;
if (!global_option_disable_name_matching) {
if (namehead) {
insertName(newname, namehead);
} else {
namehead = newname;
}
}
if (nodehead) {
addXRefSub(newnode, nodehead);
} else {
nodehead = newnode;
}
return 1;
}
int readXRefFile(char *filename, char *basepath, int force_absolute)
{
FILE *fp;
char line[4098];
int ret = 1;
if (!((fp = fopen(filename, "r")))) {
return 0;
}
while (1) {
if (fgets(line, 4096, fp) == NULL)
break;
if (line[strlen(line) - 1] != '\n') {
fprintf(ERRS, "warning: ridiculously long line in xref file.\n");
ret = 0;
} else {
line[strlen(line) - 1] = '\0';
}
if (!addXRefFromLine(line, basepath, force_absolute))
ret = 0;
}
fclose(fp);
return ret;
}
void writeXRefFileSub(xrefnode_t node, FILE * fp)
{
if (!node)
return;
writeXRefFileSub(node->left, fp);
if (!node->fromseed) {
char *currentpath;
if (!global_basepath_set) {
safe_asprintf(¤tpath, "%s", node->filename); if (!currentpath) { fprintf(stderr, "Out of memory.\n"); exit(1); }
} else {
currentpath = relpath(nodefilenamerealpath(node), global_basepath, 1); }
fprintf(fp, "%s%c%s%c%s\n",
currentpath, 1, node->xref, 1, node->title ? node->title : "");
free(currentpath);
}
writeXRefFileSub(node->dup, fp);
writeXRefFileSub(node->right, fp);
}
char *installedpath(char *filename)
{
char *realpath_var = realpath(filename, NULL);
char *currentpath = relpath(realpath_var, global_basepath, 1); char *relativeto = global_installedpath ? global_installedpath : global_basepath;
char *installedpath = NULL;
if (!global_installedpath) {
free(currentpath);
return realpath_var;
}
free(realpath_var);
safe_asprintf(&installedpath, "%s%s%s", relativeto, ((relativeto[strlen(relativeto)-1] == '/' || currentpath[0] == '/') ? "" : "/"), filename);
if (!installedpath) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(currentpath);
return installedpath;
}
void writeXRefFile(char *filename, char *indir)
{
FILE *fp;
char *outfile = "/tmp/xref_out";
if (filename)
outfile = filename;
char *cwd = getcwd(NULL, 0);
if (chdir(indir)) {
fprintf(stderr, "Could not change to directory \"%s\"\n", indir);
perror(indir);
exit(-1);
}
fprintf(stderr, "Writing cross-references to file %s\n", outfile);
if (!((fp = fopen(outfile, "w")))) {
fprintf(stderr, "Could not write cros-references to file.\n");
perror("resolvelinks");
if (chdir(cwd)) {
fprintf(stderr, "Could not change to directory \"%s\"\n", indir);
perror(indir);
exit(-1);
}
free(cwd);
return;
}
if (chdir(cwd)) {
fprintf(stderr, "Could not change to directory \"%s\"\n", indir);
perror(indir);
exit(-1);
}
free(cwd);
if (!filename) fprintf(fp, "Cross-references seen (for debugging only)\n\n");
writeXRefFileSub(nodehead, fp);
fclose(fp);
}
char *nodepath(xrefnode_t node)
{
if (!node->fullpath) {
char *retval;
if (isURI(node->filename)) {
retval = strdup(node->filename);
} else if (node->basepath[0]) {
safe_asprintf(&retval, "%s%s%s", node->basepath, ((node->basepath[strlen(node->basepath)-1] == '/' || node->filename[0] == '/') ? "" : "/"), node->filename);
if (!retval) { fprintf(stderr, "Out of memory.\n"); exit(1); }
} else {
retval = nodefilenamerealpath(node);
}
node->fullpath = retval;
}
return node->fullpath;
}
void addXRefSub(xrefnode_t newnode, xrefnode_t tree)
{
int pos = strcmp(newnode->xref, tree->xref);
if (!newnode->title)
newnode->title = strdup("");
if (pos < 0) {
if (tree->left)
addXRefSub(newnode, tree->left);
else {
tree->left = newnode;
newnode->parent = tree;
rebalance_xrefnode_t_tree(tree, newnode);
}
} else if (!pos) {
xrefnode_t iter;
int drop = 0;
for (iter = tree; iter; iter = iter->dup) {
if (!strcmp(nodepath(newnode), nodepath(iter))) {
if (!strcmp(newnode->title, iter->title)) {
drop = 1;
if (!newnode->fromseed)
iter->fromseed = 0;
}
}
if (debugging)
printf("Dup: %s %s %s == %s %s %s\n", iter->title,
nodepath(iter), iter->xref, newnode->title,
nodepath(newnode), newnode->xref);
if (!iter->dup) {
if (!drop) {
iter->dup = newnode;
}
if (!iter->fromseed) {
duplicates++;
openlogfile();
if (logfile) {
fprintf(logfile, "Dup: %s %s %s == %s %s %s\n",
iter->title, nodepath(iter), iter->xref,
newnode->title, nodepath(newnode),
newnode->xref);
}
}
break;
}
}
} else {
if (tree->right)
addXRefSub(newnode, tree->right);
else {
tree->right = newnode;
newnode->parent = tree;
rebalance_xrefnode_t_tree(tree, newnode);
}
}
}
void addXRef(xmlNode * node, char *filename)
{
xrefnode_t newnode;
char *tempstring;
char *bufptr;
char *nextstring;
if (!node) {
printf("warning: addXRef called on null node\n");
return;
}
bufptr = proptext("name", node->properties);
if (!bufptr) {
printf("warning: addXRef called on anchor with no name property\n");
}
if (debugging) {
printf("STRL " FMT_SIZE_T "\n", strlen(bufptr));
fflush(stdout);
}
tempstring = bufptr;
while (tempstring && *tempstring) {
for (nextstring = tempstring; *nextstring && (*nextstring != ' ');
nextstring++);
if (*nextstring) {
*nextstring = '\0';
nextstring++;
} else {
nextstring = NULL;
}
newnode = malloc(sizeof(*newnode));
if (!newnode) {
fprintf(stderr, "Out of memory in addXRef[2]\n");
exit(-1);
}
if (strlen(tempstring)) {
newnode->basepath = "";
newnode->filename = filename;
#ifdef RP_CACHE
newnode->filename_rp = NULL;
#endif
newnode->fullpath = NULL;
safe_asprintf(&newnode->xref, "%s", tempstring);
if (!newnode->xref) { fprintf(stderr, "Out of memory.\n"); exit(1); }
newnode->title = proptext("title", node->properties);
newnode->left = NULL;
newnode->right = NULL;
newnode->parent = NULL;
newnode->maxleftdepth = 0;
newnode->maxrightdepth = 0;
newnode->dup = NULL;
newnode->fromseed = seeding_in_progress;
newnode->force_absolute = 0;
xrefbyname_t newname = malloc(sizeof(*newname));
newname->name = nameFromAppleRef(newnode->xref);
newname->node = newnode;
newname->left = NULL;
newname->right = NULL;
newname->dup = NULL;
newname->parent = NULL;
newname->maxleftdepth = 0;
newname->maxrightdepth = 0;
newname->parent = NULL;
if (namehead) {
insertName(newname, namehead);
} else {
namehead = newname;
}
if (nodehead) {
addXRefSub(newnode, nodehead);
} else {
nodehead = newnode;
}
tempstring = nextstring;
}
}
free(bufptr); }
void gatherXRefs(xmlNode * node, htmlDocPtr dp, char *filename)
{
if (!node)
return;
if (node->name && !strcmp((char *) node->name, "a")) {
char *pt = proptext("name", node->properties);
char *pos = pt;
while (pos && *pos && *pos == ' ')
pos++;
if (pos) {
if (debugging)
printf("MAYBE: %s\n", pos);
if ((pos[0] == '/') && (pos[1] == '/')) {
if (debugging)
printf("YO: %s\n", pos);
addXRef(node, filename);
} else {
if (debugging)
printf("NOT A REF\n");
}
}
free(pt);
}
gatherXRefs(node->children, dp, filename);
gatherXRefs(node->next, dp, filename);
}
int isStartOfLinkRequest(char *text)
{
int retval = isCommentedAnchor(text);
if (debug_reparent) {
printf("isSOLR: %s: %d\n", text, retval);
}
return retval;
}
int isEndOfLinkRequest(char *text)
{
char *ptr = text;
if (!ptr)
return 0;
while (ptr && *ptr == ' ')
ptr++;
if (ptr[0] != '/' || ptr[1] != 'a' || ptr[2] != ' ') {
return 0;
}
if (debugging)
printf("ENDLINK\n");
return 1;
}
void resolveLinks(xmlNode * node, htmlDocPtr dp, char *filename, char *filename_rp)
{
while (node) {
if (node->name && !strcmp((char *) node->name, "comment")) {
if (debugging) {
printf("comment: \"%s\"\n", node->content);
}
if (isStartOfLinkRequest((char *) node->content)) {
xmlNode *close = NULL;
struct nodelistitem *nodelisthead = NULL;
struct nodelistitem *nodelistiterator = NULL;
struct nodelistitem *orignodelisthead = NULL;
if (debugging || debug_reparent)
printf("SOLR\n");
if (node->next) {
nodelisthead = nodelist("comment", node->next);
orignodelisthead = nodelisthead;
while (nodelisthead && nodelisthead->next)
nodelisthead = nodelisthead->next;
}
nodelistiterator = nodelisthead;
while (nodelistiterator && !close) {
if (debugging || debug_reparent)
printf("NODE: %s\n", nodelistiterator->node->name);
if (debugging || debug_reparent)
printf("NODETEXT: %s\nEONODETEXT\n",
nodelistiterator->node->
content ? (char *) nodelistiterator->node->
content : "(null)");
if (nodelistiterator->node->name
&& !strcmp((char *) nodelistiterator->node->name,
"comment")
&& isEndOfLinkRequest((char *) nodelistiterator->
node->content)) {
if (debugging || debug_reparent)
printf("Is EOLR\n");
close = nodelistiterator->node;
} else {
if (debugging || debug_reparent) {
printf("No match. Content was \"%s\"\n",
nodelistiterator->node->content);
}
}
nodelistiterator = nodelistiterator->prev;
}
while (orignodelisthead) {
struct nodelistitem *temp = orignodelisthead->next;
free(orignodelisthead);
orignodelisthead = temp;
}
if (close) {
if (debug_reparent) {
printf("Printing nodes between start and end.\n");
printNodeRange(node, close);
printf
("Done printing nodes between start and end.\n");
}
xmlNode *clone = xmlNodeForComment(node->content, dp);
char *lp = proptext("logicalPath", clone->properties);
char *frametgt = proptext("target", node->properties);
int retarget = (!frametgt || !strlen(frametgt));
char *target =
resolve(lp, filename, &retarget, &frametgt);
if (debugging)
printf
("RETARGET SHOULD HAVE BEEN %d (frametgt is %p)\n",
retarget, frametgt);
if (debugging)
printf("EOLR\n");
if (debugging) {
printf("LP: \"%s\"\n", lp);
}
if (target) {
xmlNode *lastnode;
int samelevel = 0;
resolved++;
if (debugging)
printf("FOUND!\n");
samelevel = onSameLevel(node, close);
if (samelevel) {
lastnode = close->prev;
xmlUnlinkNode(close);
xmlFreeNode(close);
} else {
xmlNode *newnode = xmlNewText((xmlChar *)(""));
if (!newnode) {
fprintf(stderr, "Could not allocate memory in resolveLinks()\n");
exit(-1);
}
xmlReplaceNode(close, newnode);
xmlFreeNode(close);
close = newnode;
lastnode = close->parent;
while (lastnode && !onSameLevel(lastnode, node)) {
lastnode = lastnode->parent;
}
}
if (lastnode) {
xmlNode *iter;
if (debugging)
printf("LAST NODE FOUND.\n");
xmlReplaceNode(node, clone);
xmlFreeNode(node);
node = clone;
clone = NULL;
xmlNodeSetName(node, (xmlChar *)("a"));
node->type = XML_ELEMENT_NODE;
addAttribute(node, "href", target);
addAttribute(node, "logicalPath", lp);
if (frametgt) {
if (debugging)
printf("FRAMETGT FOUND\n");
addAttribute(node, "target", frametgt);
} else {
if (debugging)
printf("NO FRAMETGT FOUND\n");
char *pos;
int index = 1;
pos = target;
while (*pos && *pos != '?' && *pos != '#')
pos++;
if (pos < target + 10) {
index = 0;
} else {
pos -= 10;
if (strncmp(pos, "index.html", 10))
index = 0;
}
if (index) {
addAttribute(node, "target", "_top");
}
}
if (node->content) free(node->content);
node->content = NULL;
node->children = node->next;
if (node->children != NULL) {
node->children->prev = NULL;
for (iter = node->children;
iter && iter != lastnode;
iter = iter->next) {
iter->parent = node;
if (debug_reparent) {
printf("REPARENTING: \n");
writeFile_sub(node, dp, stdout, 1);
printf("DONE REPARENTING\n");
}
}
if (iter != lastnode) {
fprintf(stderr,
"warning: reparenting failed.\n");
}
} else {
fprintf(stderr,
"error: some @link is missing the second field; see resulting file %s to find out which one.\n",
filename);
}
lastnode->parent = node;
if (lastnode->next) {
lastnode->next->prev = node;
}
node->next = lastnode->next;
lastnode->next = NULL;
} else {
fprintf(ERRS,
"NESTING PROBLEM: close link request marker nested higher than open marker.\n");
fprintf(ERRS, "Giving up.\n");
}
free(target);
} else {
char *full_filename = strdup(filename_rp ? filename_rp : filename);
char *printed_filename = full_filename;
if (!full_filename) {
fprintf(stderr, "Out of memory in resolveLinks[1]\n");
exit(-1);
}
if (warn_each)
fprintf(ERRS,
"\n%s:0: error: unable to resolve link %s.\n",
printed_filename, lp);
unresolved++;
char *machineGenerated = proptext("machineGenerated", clone->properties);
if (!machineGenerated || strcmp(machineGenerated, "true")) {
unresolved_explicit++;
}
if (debugging) printf("machineGenerated: %s\n", machineGenerated ? machineGenerated : "(null)");
openlogfile();
if (logfile) {
if ((!machineGenerated) || (strcmp(machineGenerated, "true"))) {
fprintf(logfile, "Unresolved: %s %s\n", printed_filename, lp);
} else {
fprintf(logfile, "Unresolved (machine-generated): %s %s\n", printed_filename, lp);
}
}
free(full_filename);
if (machineGenerated) free(machineGenerated);
}
free(frametgt);
free(lp);
if (clone) { xmlFreeNode(clone); }
} else {
char *full_filename = malloc(PATH_MAX * sizeof(char));
char *printed_filename = full_filename;
if (!full_filename) {
fprintf(stderr, "Out of memory in resolveLinks[2]\n");
exit(-1);
}
if (!realpath(filename, full_filename)) printed_filename = filename;
fprintf(ERRS,
"\n%s:0: error: broken link. No closing link request comment found.\n",
printed_filename);
openlogfile();
if (logfile) {
fprintf(logfile,
"Broken link. No closing link request comment found in \"%s\". Open comment was:\n%s\n",
printed_filename, node->content);
}
free(full_filename);
broken++;
}
}
} else if (node->name && !strcmp((char *) node->name, "a")) {
int retarget = (!has_target(node));
char *lp = proptext("logicalPath", node->properties);
if (lp) {
char *frametgt = proptext("target", node->properties);
char *target = resolve(lp, filename, &retarget, &frametgt);
free(frametgt);
if (target) {
if (debugging)
printf("FOUND!\n");
addAttribute(node, "href", target);
resolved++;
free(target);
} else {
xmlNode *iter = node->children, *tailnode;
char *full_filename = malloc(PATH_MAX * sizeof(char));
char *printed_filename = full_filename;
if (!full_filename) {
fprintf(stderr, "Out of memory in resolveLinks[3]\n");
exit(-1);
}
if (!realpath(filename, full_filename)) printed_filename = filename;
free(full_filename);
if (warn_each)
fprintf(ERRS,
"%s:0: error: unable to resolve link %s.\n",
printed_filename, lp);
unresolved++;
char *machineGenerated = proptext("machineGenerated", node->properties);
if (!machineGenerated || strcmp(machineGenerated, "true")) {
unresolved_explicit++;
}
if (debugging) printf("machineGenerated: %s\n", machineGenerated ? machineGenerated : "(null)");
openlogfile();
if (logfile) {
if ((!machineGenerated) || (strcmp(machineGenerated, "true"))) {
fprintf(logfile, "Unresolved: %s %s\n", printed_filename, lp);
} else {
fprintf(logfile, "Unresolved (machine-generated): %s %s\n", printed_filename, lp);
}
}
if (machineGenerated) free(machineGenerated);
if (debugging)
printf("Disabling link\n");
tailnode = xmlNewComment((xmlChar *)(" /a ")); if (!tailnode) {
fprintf(stderr, "Out of memory in resolveLinks[4]\n");
exit(-1);
}
xmlNode *headnode = node;
while (iter && iter->next) {
xmlNode *next = iter->next;
xmlUnlinkNode(iter);
xmlAddNextSibling(node, iter);
if (debug_reparent) {
printf("REPARENTING: \n");
writeFile_sub(node, dp, stdout, 1);
printf("DONE REPARENTING\n");
}
node = iter;
iter = next;
}
if (iter) {
xmlUnlinkNode(iter);
if (!xmlAddNextSibling(node, iter)) {
fprintf(stderr, "Add Next failed!\n");
}
node = iter;
}
xmlAddNextSibling(node, tailnode);
node = headnode;
char *tempPropString = propString(headnode);
xmlNode *newhead = xmlNewComment((xmlChar *)tempPropString);
free(tempPropString);
if (!xmlReplaceNode(node, newhead)) {
fprintf(stderr, "Replace failed!\n");
exit(-1);
}
xmlFreeNode(node);
node = newhead;
if (debugging)
printf("PS: \"%s\"\n", node->content);
}
free(lp);
} else {
if (debugging)
printf("Not a logicalPath link. Skipping.\n");
plain++;
}
} else {
if (debugging) {
printf("%s: \"%s\"\n", node->name, node->content);
}
}
resolveLinks(node->children, dp, filename, filename_rp);
node = node->next;
}
}
int onSameLevel(xmlNode * a, xmlNode * b)
{
xmlNode *iter;
if (a == b)
return -1;
for (iter = a->prev; iter; iter = iter->prev) {
if (iter == b) {
return 1;
}
}
for (iter = a->next; iter; iter = iter->next) {
if (iter == b) {
return 1;
}
}
return 0;
}
void writeFile(xmlNode * node, htmlDocPtr dp, char *filename)
{
#ifdef OLD_CODE
FILE *fp;
if (debugging)
writeFile_sub(node, dp, stdout, 0);
if (!((fp = fopen(filename, "w")))) {
fprintf(ERRS, "error: could not open file %s for writing\n",
filename);
exit(-1);
}
writeFile_sub(node, dp, fp, 0);
fclose(fp);
#else
if (!htmlGetMetaEncoding(dp)) {
htmlSetMetaEncoding(dp, (unsigned char *) "ascii");
}
int ret = htmlSaveFile(filename, dp);
if (ret <= 0) {
fprintf(ERRS, "Failed to save file \"%s\"\n", filename);
perror("xmlSaveFile");
fprintf(stderr, "PRINTING TREE DUMP\n");
writeFile_sub(xmlDocGetRootElement(dp), dp, stdout, 1);
exit(-1);
}
#endif
}
char *propString(xmlNode * node)
{
xmlAttr *prop = node->properties;
char *propstring;
size_t len = 1;
while (prop) {
if (prop->children) {
len +=
strlen((char *) prop->name) +
strlen((char *) prop->children->content) + 4;
}
prop = prop->next;
}
len += 5;
propstring = malloc(len * sizeof(char));
if (!propstring) {
fprintf(stderr, "Out of memory in propString\n");
exit(-1);
}
strlcpy(propstring, "a", len);
prop = node->properties;
while (prop) {
if (prop->children) {
strlcat(propstring, " ", len);
strlcat(propstring, (char *) prop->name, len);
strlcat(propstring, "=\"", len);
strlcat(propstring, (char *) prop->children->content, len);
strlcat(propstring, "\"", len);
}
prop = prop->next;
}
return propstring;
}
void writeProps(xmlNode * node, FILE * fp)
{
xmlAttr *prop = node->properties;
while (prop) {
if (prop->children) {
fprintf(fp, " %s=\"%s\"", prop->name, prop->children->content);
}
prop = prop->next;
}
}
void writeFile_sub(xmlNode * node, htmlDocPtr dp, FILE * fp,
int this_node_and_children_only)
{
if (!node)
return;
if (node->name) {
if (!strcmp((char *) node->name, "text")) {
char *temp = xmlNodeGetRawString(dp, node, 0);
fprintf(fp, "%s", temp);
free(temp);
} else if (!strcmp((char *) node->name, "comment")) {
fprintf(fp, "<!--%s-->", node->content);
} else {
fprintf(fp, "<%s", node->name);
writeProps(node, fp);
fprintf(fp, ">");
}
}
writeFile_sub(node->children, dp, fp, 0);
if (strcmp((char *) node->name, "text")
&& strcmp((char *) node->name, "comment")) {
fprintf(fp, "</%s>", node->name);
}
if (!this_node_and_children_only)
writeFile_sub(node->next, dp, fp, 0);
return;
}
xmlNode *nodematching(char *name, xmlNode * cur, int recurse)
{
xmlNode *temp = NULL;
while (cur) {
if (!cur->name)
break;
if (!strcasecmp((char *) cur->name, name))
break;
if (recurse) {
if ((temp = nodematching(name, cur->children, recurse))) {
return temp;
}
}
cur = cur->next;
}
return cur;
}
char *textmatching(char *name, xmlNode * node, int missing_ok, int recurse)
{
xmlNode *cur = nodematching(name, node, recurse);
char *ret = NULL;
if (!cur) {
if (!missing_ok) {
fprintf(ERRS, "Invalid or missing contents for %s.\n", name);
}
} else if (cur && cur->children && cur->children->content) {
ret = (char *) cur->children->content;
} else if (!strcasecmp(name, "text")) {
ret = (char *) cur->content;
} else {
fprintf(ERRS, "Missing/invalid contents for %s.\n", name);
}
return ret;
}
char *proptext(char *name, struct _xmlAttr *prop)
{
for (; prop; prop = prop->next) {
if (!strcasecmp((char *) prop->name, name)) {
if (prop->children && prop->children->content) {
return strdup((char *) prop->children->content);
}
}
}
return NULL;
}
int propval(char *name, struct _xmlAttr *prop)
{
for (; prop; prop = prop->next) {
if (!strcasecmp((char *) prop->name, name)) {
if (prop->children && prop->children->content) {
return atoi((char *) prop->children->content);
}
}
}
return 0;
}
void quietErrors(void *userData, xmlErrorPtr error)
{
}
xmlNode *findAnchor(xmlNode *node)
{
if (!node) return NULL;
if (node->name && !strcmp((char *) node->name, "a")) {
return node;
}
xmlNode *temp = findAnchor(node->children);
if (temp) return temp;
return findAnchor(node->next);
}
xmlNode *xmlNodeForComment(xmlChar *commentguts, htmlDocPtr parentDoc)
{
char *data = NULL;
if (!commentguts) return NULL;
while ((*commentguts == ' ') || (*commentguts == '\t')) {
commentguts++;
}
safe_asprintf(&data, "<html><body><p><%s></a>", commentguts);
if (!data) { fprintf(stderr, "Out of memory.\n"); exit(1); }
char *encoding = (char *)htmlGetMetaEncoding(parentDoc);
if (!encoding) encoding = "ascii";
htmlParserCtxtPtr context = htmlCreateMemoryParserCtxt((char *)"<p></p>", 7);
xmlSetStructuredErrorFunc(context, quietErrors);
htmlDocPtr dp = htmlCtxtReadDoc(context, (xmlChar *)data, "", encoding, HTML_PARSE_NOWARNING|HTML_PARSE_NOERROR | HTML_PARSE_RECOVER);
xmlNode *node = findAnchor(xmlDocGetRootElement(dp));
xmlNode *retval = node ? xmlCopyNode(node, 2) : NULL;
xmlFreeDoc(dp);
free(data);
htmlFreeParserCtxt(context);
return retval;
}
int isCommentedAnchor(char *commentString)
{
char *ptr = commentString;
if (!commentString)
return 0;
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')
ptr++;
if (*ptr != 'a')
return 0;
ptr++;
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')
ptr++;
return strstr(ptr, "logicalPath") ? 1 : 0;
}
char *xmlNodeGetRawString(htmlDocPtr dp, xmlNode * node, int whatever)
{
xmlNode copynode;
bcopy(node, ©node, sizeof(copynode));
copynode.next = NULL;
return (char *) xmlNodeListGetRawString(dp, ©node, whatever);
}
refparts_t getrefparts(char *origref, int parts)
{
char *refpart = origref;
char *langpart;
char *rest;
char *refpartcopy = NULL, *langpartcopy = NULL;
struct refparts *retval = malloc(sizeof(*retval));
int refpartlen = 0, langpartlen = 0;
if (origref[0] != '/' || origref[0] != '/') {
free(retval);
return NULL;
}
refpart += 2;
langpart = refpart;
while (*langpart && *langpart != '/') {
langpart++;
refpartlen++;
}
langpart++;
rest = langpart;
while (*rest && *rest != '/') {
rest++;
langpartlen++;
}
rest++;
if (!strlen(refpart) || !strlen(langpart) || !strlen(rest)) {
fprintf(ERRS,
"warning: malformed apple_ref %s has less than three parts.\n",
origref);
free(retval);
return NULL;
}
if (parts & PART_REF) {
refpartcopy = malloc((refpartlen + 1) * sizeof(char)); if (!refpartcopy) {
fprintf(stderr, "Out of memory in getrefparts[1]\n");
exit(-1);
}
strncpy(refpartcopy, refpart, refpartlen);
refpartcopy[refpartlen] = '\0';
retval->refpart = refpartcopy;
} else {
retval->refpart = NULL;
}
if (parts & PART_LANG) {
langpartcopy = malloc((langpartlen + 1) * sizeof(char)); if (!langpartcopy) {
fprintf(stderr, "Out of memory in getrefparts[2]\n");
exit(-1);
}
strncpy(langpartcopy, langpart, langpartlen);
langpartcopy[langpartlen] = '\0';
retval->langpart = langpartcopy;
} else {
retval->langpart = NULL;
}
if (parts & PART_REST) {
safe_asprintf(&retval->rest, "%s", rest);
if (!retval->rest) { fprintf(stderr, "Out of memory.\n"); exit(1); }
} else {
retval->rest = NULL;
}
return retval;
}
char *refRefChange(char *ref, char *extref)
{
refparts_t rp = getrefparts(ref, PART_ALL);
char *langpart, *rest, *retval;
if (!rp)
return NULL;
langpart = rp->langpart;
rest = rp->rest;
safe_asprintf(&retval, "//%s/%s/%s", extref, langpart, rest);
if (!retval) { fprintf(stderr, "Out of memory.\n"); exit(1); }
refpartsfree(rp);
return retval;
}
char *refLangChange(char *ref, char *lang)
{
refparts_t rp = getrefparts(ref, PART_ALL);
char *refpart, *rest, *retval;
if (!rp)
return NULL;
refpart = rp->refpart;
rest = rp->rest;
safe_asprintf(&retval, "//%s/%s/%s", refpart, lang, rest); if (!retval) {
fprintf(stderr, "Out of memory in refLangChange\n");
exit(-1);
}
refpartsfree(rp);
return retval;
}
char *makeurl(char *rawfilename, char *offset, int retarget, int relativeToInput)
{
#if 1
char *filename = rawfilename;
char *buf = NULL;
char *relpath_var = NULL;
if (relativeToInput) {
char *realpath_var = strdup(rawfilename); relpath_var = relpath(realpath_var, inputDirectory, 1);
free(realpath_var);
filename = relpath_var;
}
char *dir = ts_dirname(filename);
char *base = ts_basename(filename);
char *updir = ts_dirname(dir);
char *upbase = ts_basename(dir);
char *indexpath = NULL;
if (isURI(rawfilename)) {
if (relpath_var) free(relpath_var);
free(dir);
free(base);
free(updir);
free(upbase);
return strdup(rawfilename);
}
if (debugging)
printf("RETARGET (INITIAL): %d\n", retarget);
safe_asprintf(&indexpath, "%s/index.html", updir);
if (!indexpath) { fprintf(stderr, "Out of memory.\n"); exit(1); }
if (retarget
&& (!strcmp(base, "index.html")
|| !strcmp(base, "CompositePage.html"))) {
if (debugging)
printf("Going to an index.html file. Not retargetting.\n");
if (debugging)
printf("FILENAME: %s\nOFFSET: %s\n", filename, offset);
retarget = 0;
if (indexpath) free(indexpath);
indexpath = NULL;
}
if (retarget && !ishdindex(indexpath)) {
if (debugging)
fprintf(stderr, "\nNo index found at %s.\n", indexpath);
if (debugging)
fprintf(stderr,
"DIR: %s\nBASE: %s\nUPDIR: %s\nUPBASE: %s\nORIG_FILENAME: %s\n",
dir, base, updir, upbase, filename);
retarget = 0;
if (indexpath) free(indexpath);
indexpath = NULL;
}
if (debugging)
printf("RETARGET: %d\n", retarget);
if (retarget) {
safe_asprintf(&buf, "%s?%s/%s#%s", indexpath, upbase, base, offset);
if (!buf) { fprintf(stderr, "Out of memory.\n"); exit(1); }
} else {
safe_asprintf(&buf, "%s#%s", filename, offset);
if (!buf) { fprintf(stderr, "Out of memory.\n"); exit(1); }
}
free(dir);
free(base);
free(updir);
free(upbase);
if (indexpath) free(indexpath);
if (relpath_var) free(relpath_var);
return buf;
#else
return strdup(filename);
#endif
}
int matchingPathParts(char *a, char *b, int *isbelowme)
{
char *aiter, *biter;
int found;
found = 0;
for (aiter = a; *aiter;) {
if (*aiter == '.' && *(aiter + 1) == '/') {
aiter += 2;
found = 1;
} else if (found && *aiter == '/') {
aiter++;
} else
break;
}
found = 0;
for (biter = b; *biter;) {
if (*biter == '.' && *(biter + 1) == '/') {
biter += 2;
found = 1;
} else if (found && *biter == '/') {
biter++;
} else
break;
}
found = 0;
for (;; aiter++, biter++) {
if (*aiter == '/' && !(*biter)) {
char *aiterx;
int moreslashes = 0;
for (aiterx = aiter + 1; *aiterx; aiterx++) {
if (*aiterx == '/')
moreslashes = 1;
}
*isbelowme = !moreslashes;
found++;
}
if (!(*aiter) && *biter == '/') {
char *biterx;
int moreslashes = 0;
for (biterx = biter + 1; *biterx; biterx++) {
if (*biterx == '/')
moreslashes = 1;
}
*isbelowme = !moreslashes;
found++;
}
if (*aiter != *biter) {
break;
}
if (*aiter == '/') {
found++;
}
if (!(*aiter && *biter))
break;
}
return found;
}
searchobj_t searchref(char *xref, xrefnode_t tree, int retarget, char *basepath)
{
int pos;
if (!tree)
return NULL;
while (tree) {
int partial_match = 0;
pos = strcmp(xref, tree->xref);
if (!global_option_disable_partial_ref_matching) {
if (pos) { partial_match = ispartialmatch(xref, tree->xref);
}
}
if (partial_match || !pos) {
xrefnode_t iter, maxmatchnode = NULL, maxmatchnode_nocache = NULL;
int maxmatch = -1;
int maxmatch_nocache = -1;
for (iter = tree; iter; iter = iter->dup) {
int belowme;
int match = matchingPathParts(nodepath(iter), basepath, &belowme);
if (match > maxmatch || (match == maxmatch && belowme)) {
maxmatch = match;
maxmatchnode = iter;
}
if (!iter->fromseed) {
if (match > maxmatch_nocache
|| (match == maxmatch_nocache && belowme)) {
maxmatch_nocache = match;
maxmatchnode_nocache = iter;
}
}
}
if (maxmatchnode)
tree = maxmatchnode;
if (maxmatchnode_nocache)
tree = maxmatchnode_nocache;
searchobj_t retval = malloc(sizeof(*retval));
retval->uri = makeurl(nodepath(tree), tree->xref, retarget, (tree->basepath[0] ? 0 : 1));
retval->obj = tree;
return retval;
} else if (pos < 0) {
tree = tree->left;
} else {
tree = tree->right;
}
}
return NULL;
}
char *resolve(char *xref, char *filename, int *retarget, char **frametgt)
{
searchobj_t targetobj = NULL;
char *curref = xref;
char *dn = ts_dirname(filename);
char *basepath = realpath(dn, NULL);
char *writeptr = xref;
free(dn);
if (!xref) {
free(basepath);
return NULL;
}
while (writeptr) {
while (*writeptr && *writeptr != ' ')
writeptr++;
if (*writeptr == ' ')
*writeptr = '\0';
else
writeptr = 0;
if (strlen(curref)) {
char *altLangRef1 = NULL;
char *altLangRef2 = NULL;
char *altLangRef3 = NULL;
refparts_t rp;
int i;
if (debugging) {
printf("SEARCHING FOR \"%s\"\n", curref);
}
for (i = -1; i <= nextrefs; i++) {
char *newcurref = NULL;
int freenewcurref = 0;
char *refpart;
if ((rp = getrefparts(curref, PART_ALL))) {
int isanysymbol = 0;
if (!global_option_disable_name_matching) {
if (!strncmp(rp->rest, "anysymbol/", 10)) {
xrefnode_t temp = refByName(rp->rest + 10, basepath);
if (temp && temp->xref) {
isanysymbol = 1;
safe_asprintf(&newcurref, "%s", temp->xref);
if (!newcurref) { fprintf(stderr, "Out of memory.\n"); exit(1); }
freenewcurref = 1;
}
}
}
if (!isanysymbol) {
if (i == nextrefs) {
refpart = strdup("apple_ref");
} else if (i == -1) {
refpart = strdup(rp->refpart);
} else {
refpart = strdup(extrefs[i]);
}
newcurref = refRefChange(curref, refpart);
free(refpart);
freenewcurref = 1;
if (!strcmp(rp->langpart, "cpp") ||
!strcmp(rp->langpart, "occ") ||
!strcmp(rp->langpart, "C")) {
altLangRef1 = refLangChange(newcurref, "c");
altLangRef2 = refLangChange(newcurref, "cpp");
altLangRef3 = refLangChange(newcurref, "occ");
}
}
refpartsfree(rp);
} else {
newcurref = curref;
}
if (altLangRef1 && debugging) {
printf("ALSO SEARCHING FOR \"%s\"\n", altLangRef1);
}
if (altLangRef2 && debugging) {
printf("ALSO SEARCHING FOR \"%s\"\n", altLangRef2);
}
if (altLangRef3 && debugging) {
printf("ALSO SEARCHING FOR \"%s\"\n", altLangRef3);
}
targetobj =
searchref(newcurref, nodehead, *retarget, basepath);
if ((!targetobj) && altLangRef1)
targetobj =
searchref(altLangRef1, nodehead, *retarget, basepath);
if ((!targetobj) && altLangRef2)
targetobj =
searchref(altLangRef2, nodehead, *retarget, basepath);
if ((!targetobj) && altLangRef3)
targetobj =
searchref(altLangRef3, nodehead, *retarget, basepath);
if (freenewcurref) free(newcurref);
if (altLangRef1) free(altLangRef1);
if (altLangRef2) free(altLangRef2);
if (altLangRef3) free(altLangRef3);
if (targetobj)
break;
}
if (targetobj) {
char *target = strdup(targetobj->uri);
if (debugging)
printf("Mapping %s to %s\n", xref, target);
if (isURI(target)) {
*retarget = 1;
if (*frametgt) free(*frametgt);
*frametgt = strdup("_top");
searchfree(targetobj);
free(basepath);
return target;
} else {
if (targetobj->obj->fromseed && (targetobj->obj->force_absolute || force_absolute_globally)) {
char *retval = strdup(target);
free(targetobj);
free(basepath);
return retval;
} else if (!targetobj->obj->fromseed) {
char *abspath = realpath(filename, NULL);
char *reltoinput = relpath(abspath, inputDirectory, 0);
char *relpath_var = relpath(target, reltoinput, 0);
searchfree(targetobj);
free(abspath);
free(reltoinput);
free(basepath);
return relpath_var;
} else {
char *ip =installedpath(filename);
char *retval = relpath(target, ip, 0);
searchfree(targetobj);
free(ip);
free(basepath);
return retval;
}
}
searchfree(targetobj);
}
}
if (writeptr) {
*writeptr = ' ';
writeptr++;
curref = writeptr;
}
}
if (debugging)
printf("Ref not found\n");
free(basepath);
return NULL;
}
void nodelist_rec(char *name, xmlNode * cur, struct nodelistitem **nl);
struct nodelistitem *nodelist(char *name, xmlNode * root)
{
struct nodelistitem *nl = NULL;
nodelist_rec(name, root, &nl);
return nl;
}
void nodelist_rec(char *name, xmlNode * cur, struct nodelistitem **nl)
{
struct nodelistitem *nli = NULL;
while (cur) {
if (cur->name && !strcmp((char *) cur->name, name)) {
nli = malloc(sizeof(*nli)); if (nli) {
nli->node = cur;
nli->next = *nl;
nli->prev = NULL;
if (*nl) {
(*nl)->prev = nli;
}
*nl = nli;
} else {
fprintf(stderr, "Out of memory in nodelist_rec\n");
exit(-1);
}
}
nodelist_rec(name, cur->children, nl);
cur = cur->next;
}
}
void addAttribute(xmlNode * node, char *attname, char *attstring)
{
xmlAttr *properties;
xmlAttrPtr myprop;
xmlAttr *delprop = NULL;
for (properties = node->properties; properties;
properties = properties->next) {
if (delprop) {
xmlUnsetProp(node, delprop->name);
delprop = NULL;
}
if (!strcasecmp((char *) properties->name, attname)) {
delprop = properties;
}
}
if (delprop) {
xmlUnsetProp(node, delprop->name);
}
myprop = xmlSetProp(node, (xmlChar *)attname, (xmlChar *)attstring);
if (!myprop) {
fprintf(stderr, "resolveLinks: out of memory in addAttribute\n");
exit(-1);
}
}
fileref_t getFiles(char *curPath)
{
fileref_t rethead = NULL, rettail = NULL;
DIR *dirp;
struct dirent *entp;
int localDebug = 0;
char *cp = curPath;
dirp = opendir(".");
if (!dirp)
return NULL;
curPath = cp;
while ((entp = readdir(dirp))) {
if (entp->d_type == DT_DIR && strcmp(entp->d_name, ".") &&
strcmp(entp->d_name, "..") && strcmp(entp->d_name, "CVS") &&
strcmp(entp->d_name, ".svn") && strcmp(entp->d_name, ".git") &&
strcmp(entp->d_name, ".bzr") && strcmp(entp->d_name, ".hg")) {
fileref_t recreturn;
char *newpath = NULL;
if (!nodot) printf(".");
fflush(stdout);
if (chdir(entp->d_name)) {
perror("resolveLinks");
return NULL;
}
safe_asprintf(&newpath, "%s/%s", curPath, entp->d_name); if (!newpath) {
perror("resolveLinks");
return NULL;
}
if (debugging || localDebug)
printf("CURPATH: \"%s\" NP: %s\n", curPath, newpath);
if (debugging || localDebug)
printf("Recursing into %s.\n", newpath);
recreturn = getFiles(newpath);
free(newpath);
if (debugging || localDebug)
printf("Recursing out.\n");
if (debugging || localDebug)
printf("OLD COUNT: %d\n", countfiles(rethead));
if (debugging || localDebug)
printf("INS COUNT: %d\n", countfiles(recreturn));
if (rettail) {
rettail->next = recreturn;
while (rettail && rettail->next) {
rettail = rettail->next;
}
if (debugging || localDebug)
printf("CONCATENATING LISTS\n");
} else {
rethead = rettail = recreturn;
while (rettail && rettail->next) {
rettail = rettail->next;
}
if (debugging || localDebug)
printf("NEW LIST\n");
}
if (debugging || localDebug)
printf("NEW COUNT: %d\n", countfiles(rethead));
if (chdir("..")) {
char *tempcwd = getcwd(NULL, MAXPATHLEN);
fprintf(stderr, "Could not change to directory \"..\" from \"%s\"\n", tempcwd);
perror("resolveLinks");
free(tempcwd);
exit(-1);
}
} else if (tailcompare(entp->d_name, ".htm")
|| tailcompare(entp->d_name, ".html")
|| tailcompare(entp->d_name, ".shtml")
|| tailcompare(entp->d_name, ".shtml")) {
if (!nodot) printf(".");
fflush(stdout);
if (debugging || localDebug)
printf("HTML FILE %s\n", entp->d_name);
fileref_t newent = malloc(sizeof(*newent));
if (!newent) {
perror("resolveLinks");
exit(-1);
}
newent->next = NULL;
strlcpy(newent->name, curPath, MAXPATHLEN);
strlcat(newent->name, "/", MAXPATHLEN);
strlcat(newent->name, entp->d_name, MAXPATHLEN);
if (rettail) {
rettail->next = newent;
rettail = newent;
} else
rethead = rettail = newent;
if (nthreads) {
newent->threadnext = threadfiles[nfiles % nthreads];
threadfiles[nfiles % nthreads] = newent;
} else {
newent->threadnext = threadfiles[0];
threadfiles[0] = newent;
}
if (debugging || localDebug)
printf("NEWCOUNT: %d\n", countfiles(rethead));
}
}
closedir(dirp);
return rethead;
}
int countfiles(fileref_t rethead)
{
fileref_t iter;
int stagecount = 0;
for (iter = rethead; iter; iter = iter->next)
stagecount++;
return stagecount;
}
int tailcompare(char *string, char *tail)
{
char *pos = &string[strlen(string) - strlen(tail)];
if (strlen(string) < strlen(tail))
return 0;
if (debugging)
printf("LENGTHS: " FMT_SIZE_T " " FMT_SIZE_T " " FMT_SIZE_T "\n",
strlen(string), strlen(tail), strlen(string) - strlen(tail));
if (debugging)
printf("Comparing: \"%s\" to \"%s\"\n", pos, tail);
if (!strcasecmp(pos, tail)) {
if (debugging)
printf("MATCH\n");
return 1;
}
return 0;
}
void print_statistics(void)
{
int i, nprocessedfiles = 0, ttlreqs = resolved + unresolved + broken;
if (nthreads) {
for (i = 0; i < nthreads; i++) {
nprocessedfiles += thread_processed_files[i];
}
} else {
nprocessedfiles = thread_processed_files[0];
}
printf
("=====================================================================\n");
printf(" Statistics:\n\n");
printf(" files: %3d\n", nfiles);
printf(" processed: %3d\n", nprocessedfiles);
printf(" total reqs: %3d\n", ttlreqs);
printf(" resolved: %3d\n", resolved);
printf(" unresolved: %3d (%d machine-generated, %d explicit)\n", unresolved, (unresolved - unresolved_explicit), unresolved_explicit);
printf(" broken: %3d\n", broken);
printf(" plain: %3d\n", plain);
printf(" duplicates: %3d\n", duplicates);
printf(" total: %3d\n", plain + broken + resolved + unresolved);
if ((ttlreqs) && (!nopercent)) {
float percent = (((float) resolved / (float) ttlreqs) * 100.0f);
printf(" %% resolved: %f\n", percent);
}
if (logname) {
printf("\nFor a detailed resolver report, see %s\n\n", logname);
}
closelogfile();
}
int round4(int k)
{
return ((k + 3) & ~0x3);
}
#undef malloc
#undef free
void *db_malloc(size_t length)
{
int *ret = malloc(round4(length) + 12);
if (!ret) {
fprintf(stderr, "Out of memory in db_malloc\n");
exit(-1);
}
ret[0] = round4(length);
ret[1] = 0xdeadbeef;
ret[(round4(length) / 4) + 2] = 0xdeadbeef;
ret++;
ret++;
return ret;
}
void db_free(void *ptr)
{
int *intptr = ptr, *intcheckptr;
char *tail = ptr;
intptr--;
if (*intptr != 0xdeadbeef) {
printf("warning: freeing region not allocated by db_malloc\n");
free(ptr);
} else {
intptr--;
tail += (*intptr);
intcheckptr = (int *) tail;
if (*intcheckptr != 0xdeadbeef) {
printf("warning: region scribbled off end\n");
}
free(intptr);
}
}
void setup_redirection(void)
{
int fail = 0, localDebug = 0;
nullfd = open("/dev/null", (O_RDWR | O_NONBLOCK), 0);
stderrfd = dup(STDERR_FILENO);
if (nullfd == -1) {
fprintf(ERRS, "warning: Could not open /dev/null!\n");
fail = 1;
}
if (stderrfd == -1) {
fprintf(ERRS, "warning: Could not dup stderr!\n");
fail = 1;
}
if (!fail && localDebug) {
printf("Dup successful\n");
}
}
void redirect_stderr_to_null(void)
{
if ((nullfd != -1) && (stderrfd != -1)) {
dup2(nullfd, STDERR_FILENO);
}
}
void restore_stderr(void)
{
if ((nullfd != -1) && (stderrfd != -1)) {
dup2(stderrfd, STDERR_FILENO);
}
}
int *partsOfPath(char *path)
{
int *list = malloc(((strlen(path) / 2) + 2) * sizeof(int)); if (!list) {
fprintf(stderr, "Out of memory in partsOfPath\n");
exit(-1);
}
int listpos = 0;
int pos = 0;
while (path && path[pos] == '/')
pos++;
list[listpos++] = pos;
while (path && path[pos]) {
if (path[pos] == '/') {
while (path[pos] == '/')
pos++;
list[listpos++] = pos;
if (path[pos] == '\0')
break;
}
pos++;
}
list[listpos] = -3;
return list;
}
char *malloccopypart(char *source, int start, int length)
{
char *ret = malloc((length + 1) * sizeof(char)); if (!ret) {
fprintf(stderr, "Out of memory in malloccopypart\n");
exit(-1);
}
strncpy(ret, &source[start], length);
ret[length] = '\0';
return ret;
}
int getHashPos(char *a)
{
char *pos = strstr(a, "#");
if (pos) {
*pos = '\0'; return pos - a;
}
return -1;
}
char *relpath(char *target, char *fromFile, int isDir)
{
char *iter = fromFile;
int pathparts = 0;
size_t alloc_len;
int *base_pathparts = partsOfPath(fromFile);
int *target_pathparts = partsOfPath(target);
int i;
int startpos_in_target = 0;
size_t fromFileLen = strlen(fromFile);
size_t targetLen = strlen(target);
for (i = 0; ((base_pathparts[i] != -3) && (target_pathparts[i] != -3));
i++) {
int start_of_base = base_pathparts[i];
int start_of_target = target_pathparts[i];
int end_of_base =
(base_pathparts[i + 1] ==
-3) ? fromFileLen : base_pathparts[i + 1];
int end_of_target =
(target_pathparts[i + 1] ==
-3) ? targetLen : target_pathparts[i + 1];
char *basepart, *targetpart;
basepart =
malloccopypart(fromFile, start_of_base,
end_of_base - start_of_base);
targetpart =
malloccopypart(target, start_of_target,
end_of_target - start_of_target);
size_t basepartLen = end_of_base - start_of_base;
size_t targetpartLen = end_of_target - start_of_target;
while (basepartLen && (basepart[basepartLen - 1] == '/'))
basepart[basepartLen-- - 1] = '\0';
while (targetpartLen && (targetpart[targetpartLen - 1] == '/'))
targetpart[targetpartLen-- - 1] = '\0';
startpos_in_target = start_of_target;
if (strcmp(basepart, targetpart)) {
int j;
for (j = i; base_pathparts[j] != -3; j++);
pathparts = j - i - 1;
free(basepart);
free(targetpart);
break;
}
free(basepart);
free(targetpart);
}
free(base_pathparts);
free(target_pathparts);
alloc_len = ((targetLen + (3 * pathparts) + 1) * sizeof(char));
iter = malloc(alloc_len); if (!iter) {
fprintf(stderr, "Out of memory in relpath\n");
exit(-1);
}
iter[0] = '\0';
while (pathparts) {
strlcat(iter, "../", alloc_len);
pathparts--;
}
strlcat(iter, &target[startpos_in_target], alloc_len);
if (debug_relpath) {
printf("OP: %s\nFN: %s\nRP: %s\n", target, fromFile, iter);
}
return fixpath(iter);
}
char *fixpath(char *name)
{
char *from_iter = name, *to_iter;
char *lastc = NULL;
int inhashorquery = 0;
if (!name) return NULL;
to_iter = name;
while (from_iter && *from_iter && *to_iter) {
if (*from_iter == '#' || *from_iter == '?') {
inhashorquery = 1;
}
if (!inhashorquery) {
while (*from_iter == '/' && lastc && *lastc == '/') {
from_iter++;
}
}
if (from_iter != to_iter) *to_iter = *from_iter;
lastc = to_iter;
from_iter++;
to_iter++;
}
*to_iter = '\0';
if (lastc && (*lastc == '/'))
*lastc = '\0';
return name;
}
int has_target(xmlNode * node)
{
char *target = proptext("target", node->properties);
char *retarget = proptext("retarget", node->properties);
if (retarget && !strcasecmp(retarget, "yes")) {
if (target) free(target);
if (retarget) free(retarget);
return 0;
}
if (target && (target[0] != '\0')) {
free(target);
return 1;
}
return 0;
}
char *ts_dirname(char *path)
{
static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
char *orig = NULL, *copy = NULL, *junk = NULL;
while (pthread_mutex_lock(&mylock));
safe_asprintf(&junk, "%s", path); if (!junk) { fprintf(stderr, "Out of memory.\n"); exit(1); }
orig = dirname(junk);
safe_asprintf(©, "%s", orig); if (!copy) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(junk);
while (pthread_mutex_unlock(&mylock));
return copy;
}
char *ts_basename(char *path)
{
static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
char *orig = NULL, *copy = NULL, *junk = NULL;
while (pthread_mutex_lock(&mylock));
safe_asprintf(&junk, "%s", path); if (!junk) { fprintf(stderr, "Out of memory.\n"); exit(1); }
orig = basename(junk);
if (orig == NULL) {
while (pthread_mutex_unlock(&mylock));
return NULL;
}
safe_asprintf(©, "%s", orig); if (!copy) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(junk);
while (pthread_mutex_unlock(&mylock));
return copy;
}
int ishdindex(char *filename)
{
FILE *fp;
if (!(fp = fopen(filename, "r")))
return 0;
int found_head = 0;
char line[4097];
while (!found_head) {
if (!fgets(line, 4096, fp)) {
fclose(fp);
return 0;
}
if (strcasestr(line, "<head")) {
found_head = 1;
}
}
while (found_head == 1) {
if (!fgets(line, 4096, fp)) {
fclose(fp);
return 0;
}
if (strcasestr
(line, "<meta name=\"generator\" content=\"HeaderDoc\"")) {
found_head = 2;
}
if (strcasestr
(line, "<meta name=\"formatter\" content=\"gatherHeaderDoc\"")) {
fclose(fp);
return 0;
}
if (strcasestr(line, "</head")) {
fclose(fp);
return 0;
}
}
while (1) {
if (!fgets(line, 4096, fp)) {
fclose(fp);
return 0;
}
if (strcasestr(line, "<frameset")) {
fclose(fp);
return 1;
}
}
fclose(fp);
return 0;
}
int printNodeRangeSub(xmlNode * start, xmlNode * end, int leading);
int printNodeRange(xmlNode * start, xmlNode * end)
{
return printNodeRangeSub(start, end, 0);
}
int printNodeRangeSub(xmlNode * start, xmlNode * end, int leading)
{
xmlNode *iter = start;
char *leadspace = malloc(leading + 1); if (!leadspace) {
fprintf(stderr, "Out of memory in printNodeRangeSub\n");
exit(-1);
}
memset(leadspace, ' ', leading);
leadspace[leading] = '\0';
while (iter && iter != end) {
printf("%sNODE: %s CONTENT: %s\n", leadspace,
iter->name ? (char *) iter->name : "(no name)",
iter->content ? (char *) iter->content : "(null)");
if (iter->children) {
printf("%sCHILDREN:\n", leadspace);
if (printNodeRangeSub(iter->children, end, leading + 8)) {
free(leadspace);
return 1;
}
}
iter = iter->next;
}
if (iter && (iter == end))
printf("%sNODE: %s CONTENT: %s\n", leadspace,
iter->name ? (char *) iter->name : "(no name)",
iter->content ? (char *) iter->content : "(null)");
free(leadspace);
return (iter == end);
}
void openlogfile()
{
if (logfile) return;
safe_asprintf(&logname, "/tmp/resolvelinks.linkreport.XXXXXXXXXXXXX");
if (!logname) { fprintf(stderr, "Out of memory.\n"); exit(1); }
int fd = mkstemp(logname);
logfile = fdopen(fd, "w");
}
void closelogfile()
{
if (logfile) fclose(logfile);
logfile = NULL;
}
void printusage()
{
fprintf(ERRS,
"\nUsage:\n\n");
fprintf(ERRS,
" resolveLinks [-s xref_seed_file] [-t nthreads] [-d debug_level] [ -r ext_ref ] <directory>\n\n");
fprintf(ERRS, "Common options are:\n");
fprintf(ERRS,
"\t-b <base path to strip from output seed file> (default /)\n");
fprintf(ERRS,
"\t-d <debug flags> (default 0)\n");
fprintf(ERRS,
"\t-i <installed path where the HTML will end up> (default is -b value)\n");
fprintf(ERRS,
"\t-r <alternative to apple_ref> (can be used multiple times)\n");
fprintf(ERRS,
"\t-s <seed file> (can be used multiple times)\n");
fprintf(ERRS,
"\t-S <base path for previous seed file> (can be used multiple times)\n");
fprintf(ERRS,
"\t-t <number of threads> (default 2)\n");
fprintf(ERRS,
"\t-x <output seed file>\n");
fprintf(ERRS,
"\nFor more details, see man resolveLinks.\n\n");
}
int isURI(char *filename)
{
if ((strncmp(filename, "file://", 7) == 0 && filename[7] != '\0') ||
(strncmp(filename, "ftp://", 6) == 0 && filename[6] != '\0') ||
(strncmp(filename, "http://", 7) == 0 && filename[7] != '\0') ||
(strncmp(filename, "https://", 8) == 0 && filename[8] != '\0')) {
return 1;
}
return 0;
}
char *getNextXRefPart(char *data)
{
char *retval = NULL;
char *pos;
if (!data) return NULL;
for (pos = data; pos && *pos && *pos != '/'; pos++);
if (*pos) {
retval = pos+1;
}
*pos = '\0';
return retval;
}
char *nameFromAppleRef(char *ref)
{
char *refcopy;
char *ret = NULL;
refparts_t rp;
safe_asprintf(&refcopy, "%s", ref);
if (!refcopy) { fprintf(stderr, "Out of memory.\n"); exit(1); }
rp = getrefparts(refcopy, PART_REST);
if ((!rp) || (!rp->rest)) {
if (rp) refpartsfree(rp);
free(refcopy);
safe_asprintf(&ret, "%s", ref);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [1]\n", ret);
#endif
return ret;
}
char *symbol_type = rp->rest;
char *field1 = getNextXRefPart(rp->rest);
char *field2 = getNextXRefPart(field1);
char *field3 = getNextXRefPart(field2);
char *field4 = getNextXRefPart(field3);
char *field5 = getNextXRefPart(field4);
char *field6 = getNextXRefPart(field5);
if (!field1) {
free(refcopy);
safe_asprintf(&ret, "%s", rp->rest);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [1a]\n", ret);
#endif
refpartsfree(rp);
return ret;
}
if (!strcmp(symbol_type, "func") && field2) {
safe_asprintf(&ret, "%s", field1);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [2]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "ftmplt")) {
if (field3) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [3]\n", ret);
#endif
return ret;
}
safe_asprintf(&ret, "%s", field1);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [4]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "instm")) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [5]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "intfm")) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [6]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "intfcm")) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [7]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "intfp")) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [8]\n", ret);
#endif
return ret;
}
if (!strcmp(symbol_type, "instp")) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [9]\n", ret);
#endif
return ret;
}
if (field6) {
safe_asprintf(&ret, "%s", field6);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [10]\n", ret);
#endif
return ret;
}
if (field5) {
safe_asprintf(&ret, "%s", field5);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [11]\n", ret);
#endif
return ret;
}
if (field4) {
safe_asprintf(&ret, "%s", field4);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [12]\n", ret);
#endif
return ret;
}
if (field3) {
safe_asprintf(&ret, "%s", field3);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [13]\n", ret);
#endif
return ret;
}
if (field2) {
safe_asprintf(&ret, "%s", field2);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [14]\n", ret);
#endif
return ret;
}
safe_asprintf(&ret, "%s", field1);
if (!ret) { fprintf(stderr, "Out of memory.\n"); exit(1); }
free(refcopy);
refpartsfree(rp);
#ifdef DEBUG_NAMEFROMAPPLEREF
fprintf(stderr, "Returning %s [15] (ref was %s)\n", ret, ref);
#endif
return ret;
}
int insertName(xrefbyname_t node, xrefbyname_t tree)
{
int cmp = strcmp(node->name, tree->name);
#if NEAR_MATCH_DEBUG > 1
fprintf(stderr, "Checking for %s (%p) in %s (%p)\n", node->name, node, tree->name, tree);
#endif
if (cmp < 0) {
if (tree->left) {
return insertName(node, tree->left);
}
node->parent = tree;
tree->left = node;
rebalance_xrefbyname_t_tree(tree, node);
} else if (cmp > 0) {
if (tree->right) {
return insertName(node, tree->right);
}
node->parent = tree;
tree->right = node;
rebalance_xrefbyname_t_tree(tree, node);
} else {
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Dup found for %s\n", node->name);
#endif
if (tree->dup) {
return insertName(node, tree->dup);
}
node->parent = tree->parent;
tree->dup = node;
}
return 0;
}
xrefnode_t refByNameRec(char *name, xrefbyname_t pos, char *basepath);
xrefnode_t refByName(char *name, char *basepath)
{
return refByNameRec(name, namehead, basepath);
}
xrefnode_t refByNameRec(char *name, xrefbyname_t pos, char *basepath)
{
int cmp;
if (!pos) return NULL;
cmp = strcmp(name, pos->name);
if (!cmp) {
xrefbyname_t iter;
xrefnode_t nodeiter, maxmatchnode = NULL, maxmatchnode_nocache = NULL;
int maxmatch = -1;
int maxmatch_nocache = -1;
for (iter = pos; iter; iter = iter->dup) {
int belowme;
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Outer loop: %p (dup chain is %p)\n", iter, iter->dup);
#endif
for (nodeiter = iter->node; nodeiter; nodeiter = nodeiter->dup) {
int match = matchingPathParts(nodepath(nodeiter), basepath, &belowme);
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Inner loop: %p (dup chain is %p): match is %d maxmatch is %d\n", nodeiter, nodeiter->dup, match, maxmatch);
#endif
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Checking %s against %s\n", nodepath(nodeiter), basepath);
#endif
if (match > maxmatch || (match == maxmatch && belowme)) {
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "%d is better than %d. ", match, maxmatch);
#endif
maxmatch = match;
maxmatchnode = nodeiter;
}
if (!nodeiter->fromseed) {
if (match > maxmatch_nocache
|| (match == maxmatch_nocache && belowme)) {
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Ooh. a real one! ");
#endif
maxmatch_nocache = match;
maxmatchnode_nocache = nodeiter;
}
}
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "\n");
#endif
}
}
if (maxmatchnode) {
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Returning %p\n", maxmatchnode);
#endif
return maxmatchnode;
}
if (maxmatchnode_nocache) {
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Returning %p\n", maxmatchnode_nocache);
#endif
return maxmatchnode_nocache;
}
#ifdef NEAR_MATCH_DEBUG
fprintf(stderr, "Punting (returning %p)\n", pos->node);
#endif
return pos->node;
}
if (cmp < 0) return refByNameRec(name, pos->left, basepath);
return refByNameRec(name, pos->right, basepath);
}
int ispartialmatch(char *partial_ref, char *complete_ref)
{
char *refcopy;
refparts_t rp;
safe_asprintf(&refcopy, "%s", partial_ref);
if (!refcopy) { fprintf(stderr, "Out of memory.\n"); exit(1); }
rp = getrefparts(refcopy, PART_REST);
free(refcopy);
if (!rp) return 0;
char *symbol_type = rp->rest;
char *field1 = getNextXRefPart(rp->rest);
char *field2 = getNextXRefPart(field1);
char *field3 = getNextXRefPart(field2);
(void) getNextXRefPart(field3);
if (!strcmp(symbol_type, "instm") ||
!strcmp(symbol_type, "intfm") ||
!strcmp(symbol_type, "clm") ||
!strcmp(symbol_type, "ftmplt")) {
if (!field3) {
if (!strncmp(partial_ref, complete_ref, strlen(partial_ref))) {
refpartsfree(rp);
return 1;
}
}
}
refpartsfree(rp);
return 0;
}
void searchfree(searchobj_t obj)
{
if (obj->uri) free(obj->uri);
free(obj);
}
#define FIX_TYPE_TO_TOP(TYPE, TOP) void fix_##TYPE##_to_head(TYPE node) \
{ \
while (node) { \
if (node->left) { \
if (node->left->maxleftdepth > node->left->maxrightdepth) { \
node->maxleftdepth = node->left->maxleftdepth + 1; \
} else { \
node->maxleftdepth = node->left->maxrightdepth + 1; \
} \
} else { \
node->maxleftdepth = 0; \
} \
if (node->right) { \
if (node->right->maxleftdepth > node->right->maxrightdepth) { \
node->maxrightdepth = node->right->maxleftdepth + 1; \
} else { \
node->maxrightdepth = node->right->maxrightdepth + 1; \
} \
} else { \
node->maxrightdepth = 0; \
} \
node = node->parent; \
} \
}
FIX_TYPE_TO_TOP(xrefnode_t, nodehead);
FIX_TYPE_TO_TOP(xrefbyname_t, namehead)
#define ROTATE_RIGHTRIGHT(TYPE, TOP) TYPE rotate_##TYPE##_rightright(TYPE node) \
{ \
TYPE newtop = node->right; \
TYPE temp = node->parent; \
\
if (!node->right) { \
fprintf(stderr, "WARNING: node->right NULL in rightright (Bug in AVL tree.)\n"); \
return node; \
} \
if (!node->right->right) { \
fprintf(stderr, "WARNING: node->right->right NULL in rightright. (Bug in AVL tree.)\n"); \
return node; \
} \
\
if (testing_avl) { \
fprintf(stderr, "rightright at %p; right is %p rr is %p\n", node, node->right, node->right->right); \
} \
\
if (!temp) { \
\
TOP = newtop; \
} else if (temp->left == node) { \
\
temp->left = newtop; \
} else { \
\
temp->right = newtop; \
} \
\
newtop->parent = temp; \
node->parent = newtop; \
\
node->right = newtop->left; \
if (node->right) node->right->parent = node; \
node->maxrightdepth = newtop->maxleftdepth; \
\
newtop->left = node; \
if (node->maxleftdepth > node->maxrightdepth) { \
newtop->maxleftdepth = node->maxleftdepth + 1; \
} else { \
newtop->maxleftdepth = node->maxrightdepth + 1; \
} \
\
\
fix_##TYPE##_to_head(node); \
\
if (testing_avl) { \
\
verify_##TYPE##_tree(TOP); \
} \
\
return newtop; \
}
ROTATE_RIGHTRIGHT(xrefnode_t, nodehead)
ROTATE_RIGHTRIGHT(xrefbyname_t, namehead)
#define ROTATE_RIGHTLEFT(TYPE, TOP) TYPE rotate_##TYPE##_rightleft(TYPE node) \
{ \
TYPE oldright = node->right; \
\
if (!node->right) { \
fprintf(stderr, "WARNING: node->right NULL in rightleft. (Bug in AVL tree.)\n"); \
return node; \
} \
if (!node->right->left) { \
fprintf(stderr, "WARNING: node->right->left NULL in rightleft. (Bug in AVL tree.)\n"); \
return node; \
} \
\
if (testing_avl) { \
fprintf(stderr, "rightleft at %p; right is %p rl is %p\n", node, node->right, node->right->left); \
} \
\
node->right = oldright->left; \
node->maxrightdepth = oldright->maxleftdepth; \
\
oldright->left = node->right->right; \
oldright->maxleftdepth = node->right->maxrightdepth; \
\
oldright->parent = node->right; \
node->right->right = oldright; \
\
node->right->right->parent = node->right; \
node->right->parent = node; \
\
if (oldright->left) oldright->left->parent = oldright; \
\
if (oldright->maxrightdepth > oldright->maxleftdepth) { \
node->right->maxrightdepth = oldright->maxrightdepth + 1; \
} else { \
node->right->maxrightdepth = oldright->maxleftdepth + 1; \
} \
if (node->right->maxrightdepth > node->right->maxleftdepth) { \
node->maxrightdepth = node->right->maxrightdepth + 1; \
} else { \
node->maxrightdepth = node->right->maxleftdepth + 1; \
} \
\
\
fix_##TYPE##_to_head(node); \
\
if (testing_avl) { \
\
verify_##TYPE##_tree(TOP); \
} \
\
\
return rotate_##TYPE##_rightright(node); \
}
ROTATE_RIGHTLEFT(xrefnode_t, nodehead)
ROTATE_RIGHTLEFT(xrefbyname_t, namehead)
#define ROTATE_LEFTLEFT(TYPE, TOP) TYPE rotate_##TYPE##_leftleft(TYPE node) \
{ \
TYPE newtop = node->left; \
TYPE temp = node->parent; \
\
if (!node->left) { \
fprintf(stderr, "WARNING: node->left NULL in leftleft. (Bug in AVL tree.)\n"); \
return node; \
} \
if (!node->left->left) { \
fprintf(stderr, "WARNING: node->left->left NULL in leftleft. (Bug in AVL tree.)\n"); \
return node; \
} \
\
if (testing_avl) { \
fprintf(stderr, "leftleft at %p; left is %p ll is %p\n", node, node->left, node->left->left); \
} \
\
if (!temp) { \
\
TOP = newtop; \
} else if (temp->left == node) { \
\
temp->left = newtop; \
} else { \
\
temp->right = newtop; \
} \
\
newtop->parent = temp; \
node->parent = newtop; \
\
node->left = newtop->right; \
if (node->left) node->left->parent = node; \
node->maxleftdepth = newtop->maxrightdepth; \
\
newtop->right = node; \
if (node->maxleftdepth > node->maxrightdepth) { \
newtop->maxrightdepth = node->maxleftdepth + 1; \
} else { \
newtop->maxrightdepth = node->maxrightdepth + 1; \
} \
\
\
fix_##TYPE##_to_head(node); \
\
if (testing_avl) { \
\
verify_##TYPE##_tree(TOP); \
} \
return newtop; \
}
ROTATE_LEFTLEFT(xrefnode_t, nodehead)
ROTATE_LEFTLEFT(xrefbyname_t, namehead)
#define ROTATE_LEFTRIGHT(TYPE, TOP) TYPE rotate_##TYPE##_leftright(TYPE node) \
{ \
TYPE oldleft = node->left; \
\
if (!node->left) { \
fprintf(stderr, "WARNING: node->left NULL in leftright. (Bug in AVL tree.)\n"); \
return node; \
} \
if (!node->left->right) { \
fprintf(stderr, "WARNING: node->left->right NULL in leftright. (Bug in AVL tree.)\n"); \
return node; \
} \
\
if (testing_avl) { \
fprintf(stderr, "leftright at %p; left is %p lr is %p\n", node, node->left, node->left->right); \
} \
\
node->left = oldleft->right; \
node->maxleftdepth = oldleft->maxrightdepth; \
\
oldleft->right = node->left->left; \
oldleft->maxrightdepth = node->left->maxleftdepth; \
\
oldleft->parent = node->left; \
node->left->left = oldleft; \
node->left->left->parent = node->left; \
node->left->parent = node; \
\
if (oldleft->right) oldleft->right->parent = oldleft; \
\
if (oldleft->maxleftdepth > oldleft->maxrightdepth) { \
node->left->maxleftdepth = oldleft->maxleftdepth + 1; \
} else { \
node->left->maxleftdepth = oldleft->maxrightdepth + 1; \
} \
if (node->left->maxleftdepth > node->left->maxrightdepth) { \
node->maxleftdepth = node->left->maxleftdepth + 1; \
} else { \
node->maxleftdepth = node->left->maxrightdepth + 1; \
} \
\
\
fix_##TYPE##_to_head(node); \
\
if (testing_avl) { \
\
verify_##TYPE##_tree(TOP); \
} \
\
\
return rotate_##TYPE##_leftleft(node); \
}
ROTATE_LEFTRIGHT(xrefnode_t, nodehead)
ROTATE_LEFTRIGHT(xrefbyname_t, namehead)
#ifdef NOAVL
#define REBALANCE_TREE(TYPE) void rebalance_##TYPE##_tree(TYPE node, \
TYPE fromnode) { \
return; \
}
#else
#define REBALANCE_TREE(TYPE) void rebalance_##TYPE##_tree(TYPE node, \
TYPE fromnode) \
{ \
while (node) { \
DEPTH_TYPE balance; \
\
if (node->left == fromnode) { \
if (node->maxleftdepth < fromnode->maxleftdepth + 1) node->maxleftdepth = fromnode->maxleftdepth + 1; \
if (node->maxleftdepth < fromnode->maxrightdepth + 1) node->maxleftdepth = fromnode->maxrightdepth + 1; \
} else { \
if (node->maxrightdepth < fromnode->maxleftdepth + 1) node->maxrightdepth = fromnode->maxleftdepth + 1; \
if (node->maxrightdepth < fromnode->maxrightdepth + 1) node->maxrightdepth = fromnode->maxrightdepth + 1; \
} \
\
balance = node->maxrightdepth - node->maxleftdepth; \
\
if (balance > 1) { \
DEPTH_TYPE rightbalance = node->right->maxrightdepth - node->right->maxleftdepth; \
\
if (rightbalance >= 0) { \
node = rotate_##TYPE##_rightright(node); \
\
} else { \
node = rotate_##TYPE##_rightleft(node); \
\
} \
} else if (balance < -1) {\
DEPTH_TYPE leftbalance = node->left->maxrightdepth - node->left->maxleftdepth; \
\
if (leftbalance >= 0) { \
node = rotate_##TYPE##_leftright(node); \
\
} else { \
node = rotate_##TYPE##_leftleft(node); \
\
} \
} \
\
fromnode = node; \
node = node->parent; \
} \
}
#endif
REBALANCE_TREE(xrefnode_t)
REBALANCE_TREE(xrefbyname_t)
#ifdef NOAVL
#define VERIFY_TREE_SUB(TYPE) int verify_##TYPE##_tree_sub(TYPE node, DEPTH_TYPE *errors) \
{ \
return -1; \
}
#else
#define VERIFY_TREE_SUB(TYPE) int verify_##TYPE##_tree_sub(TYPE node, DEPTH_TYPE *errors) \
\
{ \
if (!node) return -1; \
\
\
DEPTH_TYPE maxleft = verify_##TYPE##_tree_sub(node->left, errors); \
\
DEPTH_TYPE maxright = verify_##TYPE##_tree_sub(node->right, errors); \
\
if (node->maxleftdepth != maxleft + 1) { \
fprintf(stderr, "Node %p left depth incorrect. Expected " DEPTH_TYPE_PRINTF " got " DEPTH_TYPE_PRINTF ".\n", node, maxleft+1, node->maxleftdepth); \
\
(*errors)++; \
} \
\
\
if (node->maxrightdepth != maxright + 1) { \
(*errors)++; \
fprintf(stderr, "Node %p right depth incorrect. Expected " DEPTH_TYPE_PRINTF " got " DEPTH_TYPE_PRINTF ".\n", node, maxright+1, node->maxrightdepth); \
} \
\
\
if (maxleft > maxright) return maxleft+1; \
return maxright+1; \
}
#endif
VERIFY_TREE_SUB(xrefnode_t)
VERIFY_TREE_SUB(xrefbyname_t)
#define VERIFY_TREE(TYPE) int verify_##TYPE##_tree(TYPE node) \
{ \
DEPTH_TYPE errors = 0; \
\
\
(void) verify_##TYPE##_tree_sub(node, &errors); \
\
return errors; \
}
VERIFY_TREE(xrefnode_t)
VERIFY_TREE(xrefbyname_t)
char *nodeline(char *path, char *xref, char *title)
{
static char *retval = NULL;
if (retval) free(retval);
safe_asprintf(&retval, "%s%c%s%c%s\n",
path, 1, xref, 1, title);
if (!retval) { fprintf(stderr, "Out of memory.\n"); exit(1); }
return retval;
}
int test_xrefbyname_t_tree()
{
return 0;
}
int test_xrefnode_t_tree()
{
DEPTH_TYPE retval = 0;
if (testing_avl) fprintf(stderr, "Testing AVL tree\n");
addXRefFromLine(
nodeline("path_100", "xref_100", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_200", "xref_200", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_300", "xref_300", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_250", "xref_250", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_350", "xref_350", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_400", "xref_400", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_50", "xref_50", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_75", "xref_75", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_65", "xref_65", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_68", "xref_68", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_440", "xref_440", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_460", "xref_460", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_450", "xref_450", "title"),
"/", 0);
retval += verify_xrefnode_t_tree(nodehead);
free_xrefnode_t_tree(nodehead);
free_xrefbyname_t_tree(namehead);
if (testing_avl) fprintf(stderr, "AVL tree test 1 done.\n");
addXRefFromLine(
nodeline("path_200", "xref_200", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_100", "xref_100", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_050", "xref_050", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_025", "xref_025", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_010", "xref_010", "title"),
"/", 0);
retval += verify_xrefnode_t_tree(nodehead);
free_xrefnode_t_tree(nodehead);
free_xrefbyname_t_tree(namehead);
if (testing_avl) fprintf(stderr, "AVL tree test 2 done.\n");
addXRefFromLine(
nodeline("path_200", "xref_200", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_300", "xref_300", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_400", "xref_400", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_500", "xref_500", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_600", "xref_600", "title"),
"/", 0);
retval += verify_xrefnode_t_tree(nodehead);
free_xrefnode_t_tree(nodehead);
free_xrefbyname_t_tree(namehead);
if (testing_avl) fprintf(stderr, "AVL tree test 3 done.\n");
addXRefFromLine(
nodeline("path_200", "xref_200", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_300", "xref_300", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_250", "xref_250", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_350", "xref_350", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_325", "xref_325", "title"),
"/", 0);
retval += verify_xrefnode_t_tree(nodehead);
free_xrefnode_t_tree(nodehead);
free_xrefbyname_t_tree(namehead);
if (testing_avl) fprintf(stderr, "AVL tree test 4 done.\n");
addXRefFromLine(
nodeline("path_200", "xref_200", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_100", "xref_100", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_150", "xref_150", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_050", "xref_050", "title"),
"/", 0);
addXRefFromLine(
nodeline("path_075", "xref_075", "title"),
"/", 0);
retval += verify_xrefnode_t_tree(nodehead);
free_xrefnode_t_tree(nodehead);
free_xrefbyname_t_tree(namehead);
if (testing_avl) fprintf(stderr, "AVL tree test 5 done.\n");
if (testing_avl) fprintf(stderr, "AVL tree test finished\n");
return retval ? -1 : 0;
}
void free_xrefnode_t_tree_sub(xrefnode_t node)
{
if (!node) return;
free_xrefnode_t_tree(node->left);
free_xrefnode_t_tree(node->right);
free_xrefnode_t_tree(node->dup);
if (node->filename) free(node->filename);
if (node->basepath) free(node->basepath);
if (node->xref) free(node->xref);
if (node->title) free(node->title);
if (node->fullpath) free(node->fullpath);
free(node);
}
void free_xrefnode_t_tree(xrefnode_t node)
{
free_xrefnode_t_tree_sub(node);
nodehead = NULL;
}
void free_xrefbyname_t_tree_sub(xrefbyname_t node)
{
if (!node) return;
free_xrefbyname_t_tree_sub(node->left);
free_xrefbyname_t_tree_sub(node->right);
if (node->name) free(node->name);
free(node);
}
void free_xrefbyname_t_tree(xrefbyname_t node)
{
free_xrefbyname_t_tree_sub(node);
namehead = NULL;
}
char *nodefilenamerealpath(xrefnode_t node)
{
#ifdef RP_CACHE
if (isURI(node->filename)) {
return node->filename;
}
if (!node->filename_rp) {
node->filename_rp = realpath(node->filename, NULL);
}
return node->filename_rp;
#else
return realpath(node->filename, NULL);
#endif
}
#ifdef ENABLE_CORE_DUMPS
static int EnableCoreDumps(void)
{
struct rlimit limit;
limit.rlim_cur = RLIM_INFINITY;
limit.rlim_max = RLIM_INFINITY;
return setrlimit(RLIMIT_CORE, &limit);
}
#endif
void checkDoc(xmlNode *node, xmlNode *parent, xmlNode *prev, htmlDocPtr dp)
{
while(node) {
if (node->doc != dp) {
fprintf(stderr, "WARNING: Node %p has wrong doc pointer (expected %p got %p.", node, dp, node->doc);
fprintf(stderr, " Name: %s\n", node->name);
fprintf(stderr, " Content: %s\n", node->content);
}
if (node->prev != prev) {
fprintf(stderr, "WARNING: Node %p has wrong prev pointer (expected %p got %p).", node, prev, node->prev);
fprintf(stderr, " Name: %s\n", node->name);
fprintf(stderr, " Content: %s\n", node->content);
}
if (node->parent != parent) {
fprintf(stderr, "WARNING: Node %p has wrong parent pointer (expected %p got %p).", node, parent, node->parent);
fprintf(stderr, " Name: %s\n", node->name);
fprintf(stderr, " Content: %s\n", node->content);
}
if (node->children) checkDoc(node->children, node, NULL, dp);
prev = node;
node = node->next;
}
}
int safe_asprintf(char **ret, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = vasprintf(ret, format, ap);
if (ret && (retval < 0)) {
*ret = NULL;
}
return retval;
}