mame/src/tools/src2html.c
2013-10-16 08:14:49 +00:00

904 lines
25 KiB
C

// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
MAME source code to HTML converter
****************************************************************************
Template file format:
<html header>
<!--PATH--> = insert path
<!--CONTENT--> = insert content
<html footer>
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "osdcore.h"
#include "astring.h"
#include "corefile.h"
/***************************************************************************
CONSTANTS & DEFINES
***************************************************************************/
#define PREPROC_CLASS "preproc"
#define KEYWORD_CLASS "keyword"
#define CUSTOM_CLASS "custom"
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
enum file_type
{
FILE_TYPE_INVALID,
FILE_TYPE_C,
FILE_TYPE_MAKE,
FILE_TYPE_XML,
FILE_TYPE_TEXT
};
struct ext_to_type
{
const char * extension;
file_type type;
};
struct token_entry
{
const char * token;
const char * color;
};
struct include_path
{
include_path * next;
astring path;
};
struct list_entry
{
list_entry * next;
astring name;
};
/***************************************************************************
GLOBAL VARIABLES
***************************************************************************/
static include_path *incpaths;
static const ext_to_type extension_lookup[] =
{
{ ".c", FILE_TYPE_C },
{ ".h", FILE_TYPE_C },
{ ".lst", FILE_TYPE_C },
{ ".mak", FILE_TYPE_MAKE },
{ "makefile", FILE_TYPE_MAKE },
{ ".lay", FILE_TYPE_XML },
{ ".xml", FILE_TYPE_XML },
{ ".txt", FILE_TYPE_TEXT },
};
static const token_entry dummy_token_table[] =
{
{ NULL, KEYWORD_CLASS }
};
static const token_entry c_token_table[] =
{
{ "#define", PREPROC_CLASS },
{ "#elif", PREPROC_CLASS },
{ "#else", PREPROC_CLASS },
{ "#endif", PREPROC_CLASS },
{ "#error", PREPROC_CLASS },
{ "#if", PREPROC_CLASS },
{ "#ifdef", PREPROC_CLASS },
{ "#ifndef", PREPROC_CLASS },
{ "#include", PREPROC_CLASS },
{ "#line", PREPROC_CLASS },
{ "#pragma", PREPROC_CLASS },
{ "#undef", PREPROC_CLASS },
{ "auto", KEYWORD_CLASS },
{ "bool", KEYWORD_CLASS },
{ "break", KEYWORD_CLASS },
{ "case", KEYWORD_CLASS },
{ "catch", KEYWORD_CLASS },
{ "char", KEYWORD_CLASS },
{ "class", KEYWORD_CLASS },
{ "const", KEYWORD_CLASS },
{ "const_cast", KEYWORD_CLASS },
{ "continue", KEYWORD_CLASS },
{ "default", KEYWORD_CLASS },
{ "delete", KEYWORD_CLASS },
{ "do", KEYWORD_CLASS },
{ "double", KEYWORD_CLASS },
{ "dynamic_cast", KEYWORD_CLASS },
{ "else", KEYWORD_CLASS },
{ "enum", KEYWORD_CLASS },
{ "explicit", KEYWORD_CLASS },
{ "export", KEYWORD_CLASS },
{ "extern", KEYWORD_CLASS },
{ "false", KEYWORD_CLASS },
{ "float", KEYWORD_CLASS },
{ "for", KEYWORD_CLASS },
{ "friend", KEYWORD_CLASS },
{ "goto", KEYWORD_CLASS },
{ "if", KEYWORD_CLASS },
{ "inline", KEYWORD_CLASS },
{ "int", KEYWORD_CLASS },
{ "long", KEYWORD_CLASS },
{ "mutable", KEYWORD_CLASS },
{ "namespace", KEYWORD_CLASS },
{ "new", KEYWORD_CLASS },
{ "operator", KEYWORD_CLASS },
{ "private", KEYWORD_CLASS },
{ "protected", KEYWORD_CLASS },
{ "public", KEYWORD_CLASS },
{ "register", KEYWORD_CLASS },
{ "reinterpret_cast", KEYWORD_CLASS },
{ "return", KEYWORD_CLASS },
{ "short", KEYWORD_CLASS },
{ "signed", KEYWORD_CLASS },
{ "sizeof", KEYWORD_CLASS },
{ "static", KEYWORD_CLASS },
{ "static_cast", KEYWORD_CLASS },
{ "struct", KEYWORD_CLASS },
{ "switch", KEYWORD_CLASS },
{ "template", KEYWORD_CLASS },
{ "this", KEYWORD_CLASS },
{ "throw", KEYWORD_CLASS },
{ "true", KEYWORD_CLASS },
{ "try", KEYWORD_CLASS },
{ "typedef", KEYWORD_CLASS },
{ "typeid", KEYWORD_CLASS },
{ "typename", KEYWORD_CLASS },
{ "union", KEYWORD_CLASS },
{ "unsigned", KEYWORD_CLASS },
{ "virtual", KEYWORD_CLASS },
{ "void", KEYWORD_CLASS },
{ "volatile", KEYWORD_CLASS },
{ "wchar_t", KEYWORD_CLASS },
{ "while", KEYWORD_CLASS },
/*
{ "INLINE", CUSTOM_CLASS },
{ "INT8", CUSTOM_CLASS },
{ "UINT8", CUSTOM_CLASS },
{ "INT16", CUSTOM_CLASS },
{ "UINT16", CUSTOM_CLASS },
{ "INT32", CUSTOM_CLASS },
{ "UINT32", CUSTOM_CLASS },
{ "INT64", CUSTOM_CLASS },
{ "UINT64", CUSTOM_CLASS },
{ "ARRAY_LENGTH", CUSTOM_CLASS },
*/
{ NULL, KEYWORD_CLASS }
};
/***************************************************************************
PROTOTYPES
***************************************************************************/
// core output functions
static int recurse_dir(int srcrootlen, int dstrootlen, astring &srcdir, astring &dstdir, astring &tempheader, astring &tempfooter);
static int output_file(file_type type, int srcrootlen, int dstrootlen, astring &srcfile, astring &dstfile, bool link_to_file, astring &tempheader, astring &tempfooter);
// HTML helpers
static core_file *create_file_and_output_header(astring &filename, astring &templatefile, astring &path);
static void output_footer_and_close_file(core_file *file, astring &templatefile, astring &path);
// path helpers
static astring &normalized_subpath(astring &dest, astring &path, int start);
static void output_path_as_links(core_file *file, astring &path, bool end_is_directory, bool link_to_file);
static bool find_include_file(astring &srcincpath, int srcrootlen, int dstrootlen, astring &srcfile, astring &dstfile, astring &filename);
/***************************************************************************
MAIN
***************************************************************************/
/*-------------------------------------------------
main - main entry point
-------------------------------------------------*/
void usage(const char *argv0)
{
fprintf(stderr, "Usage:\n%s <srcroot> <destroot> <template.html> [-Iincpath [-Iincpath [...]]]\n", argv0);
exit(1);
}
int main(int argc, char *argv[])
{
// loop over arguments
include_path **incpathhead = &incpaths;
astring srcdir, dstdir, tempfilename, tempheader, tempfooter;
int unadorned = 0;
for (int argnum = 1; argnum < argc; argnum++)
{
char *arg = argv[argnum];
// include path?
if (arg[0] == '-' && arg[1] == 'I')
{
*incpathhead = new include_path;
if (*incpathhead != NULL)
{
(*incpathhead)->next = NULL;
(*incpathhead)->path.cpy(&arg[2]).replacechr('/', PATH_SEPARATOR[0]);
incpathhead = &(*incpathhead)->next;
}
}
// other parameter
else if (arg[0] != '-' && unadorned == 0)
{
srcdir.cpy(arg);
unadorned++;
}
else if (arg[0] != '-' && unadorned == 1)
{
dstdir.cpy(arg);
unadorned++;
}
else if (arg[0] != '-' && unadorned == 2)
{
tempfilename.cpy(arg);
unadorned++;
}
else
usage(argv[0]);
}
// make sure we got 3 parameters
if (srcdir.len() == 0 || dstdir.len() == 0 || tempfilename.len() == 0)
usage(argv[0]);
// read the template file into an astring
UINT32 bufsize;
void *buffer;
if (core_fload(tempfilename, &buffer, &bufsize) == FILERR_NONE)
{
tempheader.cpy((const char *)buffer, bufsize);
osd_free(buffer);
}
// verify the template
if (tempheader.len() == 0)
{
fprintf(stderr, "Unable to read template file\n");
return 1;
}
int result = tempheader.find(0, "<!--CONTENT-->");
if (result == -1)
{
fprintf(stderr, "Template is missing a <!--CONTENT--> marker\n");
return 1;
}
tempfooter.cpy(tempheader).substr(result + 14, -1);
tempheader.substr(0, result);
// recurse over subdirectories
return recurse_dir(srcdir.len(), dstdir.len(), srcdir, dstdir, tempheader, tempfooter);
}
/***************************************************************************
CORE OUTPUT FUNCTIONS
***************************************************************************/
static int compare_list_entries(const void *p1, const void *p2)
{
const list_entry *entry1 = *(const list_entry **)p1;
const list_entry *entry2 = *(const list_entry **)p2;
return entry1->name.cmp(entry2->name);
}
/*-------------------------------------------------
recurse_dir - recurse through a directory
-------------------------------------------------*/
static int recurse_dir(int srcrootlen, int dstrootlen, astring &srcdir, astring &dstdir, astring &tempheader, astring &tempfooter)
{
static const osd_dir_entry_type typelist[] = { ENTTYPE_DIR, ENTTYPE_FILE };
// extract a normalized subpath
astring srcdir_subpath;
normalized_subpath(srcdir_subpath, srcdir, srcrootlen + 1);
// create an index file
astring indexname;
indexname.printf("%s%c%s", dstdir.cstr(), PATH_SEPARATOR[0], "index.html");
core_file *indexfile = create_file_and_output_header(indexname, tempheader, srcdir_subpath);
// output the directory navigation
core_fprintf(indexfile, "<h3>Viewing Directory: ");
output_path_as_links(indexfile, srcdir_subpath, true, false);
core_fprintf(indexfile, "</h3>");
// iterate first over directories, then over files
int result = 0;
for (int entindex = 0; entindex < ARRAY_LENGTH(typelist) && result == 0; entindex++)
{
osd_dir_entry_type entry_type = typelist[entindex];
// open the directory and iterate through it
osd_directory *dir = osd_opendir(srcdir);
if (dir == NULL)
{
result = 1;
break;
}
// build up the list of files
const osd_directory_entry *entry;
int found = 0;
list_entry *list = NULL;
while ((entry = osd_readdir(dir)) != NULL)
if (entry->type == entry_type && entry->name[0] != '.')
{
list_entry *lentry = new list_entry;
lentry->name.cpy(entry->name);
lentry->next = list;
list = lentry;
found++;
}
// close the directory
osd_closedir(dir);
// skip if nothing found
if (found == 0)
continue;
// allocate memory for sorting
list_entry **listarray = new list_entry *[found];
found = 0;
for (list_entry *curlist = list; curlist != NULL; curlist = curlist->next)
listarray[found++] = curlist;
// sort the list
qsort(listarray, found, sizeof(listarray[0]), compare_list_entries);
// rebuild the list
list = NULL;
while (--found >= 0)
{
listarray[found]->next = list;
list = listarray[found];
}
delete[] listarray;
// iterate through each file
for (list_entry *curlist = list; curlist != NULL && result == 0; curlist = curlist->next)
{
// add a header
if (curlist == list)
core_fprintf(indexfile, "\t<h2>%s</h2>\n\t<ul>\n", (entry_type == ENTTYPE_DIR) ? "Directories" : "Files");
// build the source filename
astring srcfile;
srcfile.printf("%s%c%s", srcdir.cstr(), PATH_SEPARATOR[0], curlist->name.cstr());
// if we have a file, output it
astring dstfile;
if (entry_type == ENTTYPE_FILE)
{
// make sure we care, first
file_type type = FILE_TYPE_INVALID;
for (int extnum = 0; extnum < ARRAY_LENGTH(extension_lookup); extnum++)
if (core_filename_ends_with(curlist->name, extension_lookup[extnum].extension))
{
type = extension_lookup[extnum].type;
break;
}
// if we got a valid file, process it
if (type != FILE_TYPE_INVALID)
{
dstfile.printf("%s%c%s.html", dstdir.cstr(), PATH_SEPARATOR[0], curlist->name.cstr());
if (indexfile != NULL)
core_fprintf(indexfile, "\t<li><a href=\"%s.html\">%s</a></li>\n", curlist->name.cstr(), curlist->name.cstr());
result = output_file(type, srcrootlen, dstrootlen, srcfile, dstfile, srcdir == dstdir, tempheader, tempfooter);
}
}
// if we have a directory, recurse
else
{
dstfile.printf("%s%c%s", dstdir.cstr(), PATH_SEPARATOR[0], curlist->name.cstr());
if (indexfile != NULL)
core_fprintf(indexfile, "\t<li><a href=\"%s/index.html\">%s/</a></li>\n", curlist->name.cstr(), curlist->name.cstr());
result = recurse_dir(srcrootlen, dstrootlen, srcfile, dstfile, tempheader, tempfooter);
}
}
// close the list if we found some stuff
if (list != NULL)
core_fprintf(indexfile, "\t</ul>\n");
// free all the allocated entries
while (list != NULL)
{
list_entry *next = list->next;
delete list;
list = next;
}
}
if (indexfile != NULL)
output_footer_and_close_file(indexfile, tempfooter, srcdir_subpath);
return result;
}
/*-------------------------------------------------
output_file - output a file, converting to
HTML
-------------------------------------------------*/
static int output_file(file_type type, int srcrootlen, int dstrootlen, astring &srcfile, astring &dstfile, bool link_to_file, astring &tempheader, astring &tempfooter)
{
// extract a normalized subpath
astring srcfile_subpath;
normalized_subpath(srcfile_subpath, srcfile, srcrootlen + 1);
fprintf(stderr, "Processing %s\n", srcfile_subpath.cstr());
// set some defaults
bool color_quotes = false;
const char *comment_start = "";
const char *comment_start_esc = "";
const char *comment_end = "";
const char *comment_end_esc = "";
const char *comment_inline = "";
const char *comment_inline_esc = "";
const char *token_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_#";
const token_entry *token_table = dummy_token_table;
// based on the file type, set the comment info
switch (type)
{
case FILE_TYPE_C:
color_quotes = true;
comment_start = comment_start_esc = "/*";
comment_end = comment_end_esc = "*/";
comment_inline = comment_inline_esc = "//";
token_table = c_token_table;
break;
case FILE_TYPE_MAKE:
color_quotes = true;
comment_inline = comment_inline_esc = "#";
break;
case FILE_TYPE_XML:
color_quotes = true;
comment_start = "<!--";
comment_start_esc = "&lt;!--";
comment_end = "-->";
comment_end_esc = "--&gt;";
break;
default:
case FILE_TYPE_TEXT:
break;
}
// make the token lookup table
bool is_token[256];
memset(is_token, 0, sizeof(is_token));
for (int toknum = 0; token_chars[toknum] != 0; toknum++)
is_token[(UINT8)token_chars[toknum]] = true;
// open the source file
core_file *src;
if (core_fopen(srcfile, OPEN_FLAG_READ, &src) != FILERR_NONE)
{
fprintf(stderr, "Unable to read file '%s'\n", srcfile.cstr());
return 1;
}
// open the output file
core_file *dst = create_file_and_output_header(dstfile, tempheader, srcfile_subpath);
if (dst == NULL)
{
fprintf(stderr, "Unable to write file '%s'\n", dstfile.cstr());
core_fclose(src);
return 1;
}
// output the directory navigation
core_fprintf(dst, "<h3>Viewing File: ");
output_path_as_links(dst, srcfile_subpath, false, link_to_file);
core_fprintf(dst, "</h3>");
// start with some tags
core_fprintf(dst, "\t<pre class=\"source\">\n");
// iterate over lines in the source file
int linenum = 1;
bool in_comment = false;
char srcline[4096];
while (core_fgets(srcline, ARRAY_LENGTH(srcline), src) != NULL)
{
// start with the line number
astring dstline;
dstline.catprintf("<span class=\"linenum\">%5d</span>&nbsp;&nbsp;", linenum++);
// iterate over characters in the source line
bool escape = false;
bool in_quotes = false;
bool in_inline_comment = false;
bool last_token_was_include = false;
bool last_was_token = false;
bool quotes_are_linked = false;
UINT8 curquote = 0;
int curcol = 0;
for (char *srcptr = srcline; *srcptr != 0; )
{
UINT8 ch = *srcptr++;
// track whether or not we are within an extended (C-style) comment
if (!in_quotes && !in_inline_comment)
{
if (!in_comment && ch == comment_start[0] && strncmp(srcptr - 1, comment_start, strlen(comment_start)) == 0)
{
dstline.catprintf("<span class=\"comment\">%s", comment_start_esc);
curcol += strlen(comment_start);
srcptr += strlen(comment_start) - 1;
ch = 0;
in_comment = true;
}
else if (in_comment && ch == comment_end[0] && strncmp(srcptr - 1, comment_end, strlen(comment_end)) == 0)
{
dstline.catprintf("%s</span>", comment_end_esc);
curcol += strlen(comment_end);
srcptr += strlen(comment_end) - 1;
ch = 0;
in_comment = false;
}
}
// track whether or not we are within an inline (C++-style) comment
if (!in_quotes && !in_comment && !in_inline_comment && ch == comment_inline[0] && strncmp(srcptr - 1, comment_inline, strlen(comment_inline)) == 0)
{
dstline.catprintf("<span class=\"comment\">%s", comment_inline_esc);
curcol += strlen(comment_inline);
srcptr += strlen(comment_inline) - 1;
ch = 0;
in_inline_comment = true;
}
// if this is the start of a new token, see if we want to color it
if (!in_quotes && !in_comment && !in_inline_comment && !last_was_token && is_token[ch])
{
const token_entry *curtoken;
char *temp = srcptr;
int toklength;
// find the end of the token
while (*temp != 0 && is_token[(UINT8)*temp])
temp++;
toklength = temp - (srcptr - 1);
// scan the token table
last_token_was_include = false;
for (curtoken = token_table; curtoken->token != NULL; curtoken++)
if (strncmp(srcptr - 1, curtoken->token, toklength) == 0 && strlen(curtoken->token) == toklength)
{
dstline.catprintf("<span class=\"%s\">%s</span>", curtoken->color, curtoken->token);
curcol += strlen(curtoken->token);
srcptr += strlen(curtoken->token) - 1;
ch = 0;
// look for include tokens specially
if (type == FILE_TYPE_C && strcmp(curtoken->token, "#include") == 0)
last_token_was_include = true;
break;
}
}
last_was_token = is_token[ch];
// if we hit a tab, expand it
if (ch == 0x09)
{
// compute how many spaces
int spaces = 4 - curcol % 4;
while (spaces--)
{
dstline.cat(' ');
curcol++;
}
}
// otherwise, copy the source character
else if (ch != 0x0a && ch != 0x0d && ch != 0)
{
// track opening quotes
if (!in_comment && !in_inline_comment && !in_quotes && (ch == '"' || ch == '\''))
{
if (color_quotes)
dstline.catprintf("<span class=\"string\">%c", ch);
else
dstline.cat(ch);
in_quotes = true;
curquote = ch;
// handle includes
if (last_token_was_include)
{
char *endquote = strchr(srcptr, ch);
if (endquote != NULL)
{
astring filename(srcptr, endquote - srcptr);
astring target;
if (find_include_file(target, srcrootlen, dstrootlen, srcfile, dstfile, filename))
{
dstline.catprintf("<a href=\"%s\">", target.cstr());
quotes_are_linked = true;
}
}
}
}
// track closing quotes
else if (!in_comment && !in_inline_comment && in_quotes && (ch == curquote) && !escape)
{
if (quotes_are_linked)
dstline.catprintf("</a>");
if (color_quotes)
dstline.catprintf("%c</span>", ch);
else
dstline.cat(ch);
in_quotes = false;
curquote = 0;
quotes_are_linked = false;
}
// else just output the current character
else if (ch == '&')
dstline.catprintf("&amp;");
else if (ch == '<')
dstline.catprintf("&lt;");
else if (ch == '>')
dstline.catprintf("&gt;");
else
dstline.cat(ch);
curcol++;
}
// Update escape state
if (in_quotes)
escape = (ch == '\\' && type == FILE_TYPE_C) ? !escape : false;
}
// finish inline comments
if (in_inline_comment)
{
dstline.catprintf("</span>");
in_inline_comment = false;
}
// append a break and move on
dstline.catprintf("\n");
core_fputs(dst, dstline);
}
// close tags
core_fprintf(dst, "\t</pre>\n");
// close the file
output_footer_and_close_file(dst, tempfooter, srcfile_subpath);
core_fclose(src);
return 0;
}
/***************************************************************************
HTML OUTPUT HELPERS
***************************************************************************/
/*-------------------------------------------------
create_file_and_output_header - create a new
HTML file with a standard header
-------------------------------------------------*/
static core_file *create_file_and_output_header(astring &filename, astring &templatefile, astring &path)
{
// create the indexfile
core_file *file;
if (core_fopen(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS | OPEN_FLAG_NO_BOM, &file) != FILERR_NONE)
return NULL;
// print a header
astring modified(templatefile);
modified.replace(0, "<!--PATH-->", path.cstr());
core_fwrite(file, modified.cstr(), modified.len());
// return the file
return file;
}
/*-------------------------------------------------
output_footer_and_close_file - write a
standard footer to an HTML file and close it
-------------------------------------------------*/
static void output_footer_and_close_file(core_file *file, astring &templatefile, astring &path)
{
astring modified(templatefile);
modified.replace(0, "<!--PATH-->", path.cstr());
core_fwrite(file, modified.cstr(), modified.len());
core_fclose(file);
}
/***************************************************************************
HTML OUTPUT HELPERS
***************************************************************************/
/*-------------------------------------------------
normalized_subpath - normalize a path to
forward slashes and extract a subpath
-------------------------------------------------*/
static astring &normalized_subpath(astring &dest, astring &path, int start)
{
return dest.cpysubstr(path, start, -1).replacechr(PATH_SEPARATOR[0], '/');
}
/*-------------------------------------------------
output_path_as_links - output a path as a
series of links
-------------------------------------------------*/
static void output_path_as_links(core_file *file, astring &path, bool end_is_directory, bool link_to_file)
{
// first count how deep we are
int srcdepth = 0;
for (int slashindex = path.chr(0, '/'); slashindex != -1; slashindex = path.chr(slashindex + 1, '/'))
srcdepth++;
if (end_is_directory)
srcdepth++;
// output a link to the root
core_fprintf(file, "<a href=\"");
for (int depth = 0; depth < srcdepth; depth++)
core_fprintf(file, "../");
core_fprintf(file, "index.html\">&lt;root&gt;</a>/");
// now output links to each path up the chain
int curdepth = 0;
int lastslash = 0;
for (int slashindex = path.chr(lastslash, '/'); slashindex != -1; slashindex = path.chr(lastslash, '/'))
{
astring substr(path, lastslash, slashindex - lastslash);
curdepth++;
core_fprintf(file, "<a href=\"");
for (int depth = curdepth; depth < srcdepth; depth++)
core_fprintf(file, "../");
core_fprintf(file, "index.html\">%s</a>/", substr.cstr());
lastslash = slashindex + 1;
}
// and a final link to the current directory
astring substr(path, lastslash, -1);
if (end_is_directory)
core_fprintf(file, "<a href=\"index.html\">%s</a>", substr.cstr());
else if (link_to_file)
core_fprintf(file, "<a href=\"%s\">%s</a>", substr.cstr(), substr.cstr());
else
core_fprintf(file, "<a href=\"%s.html\">%s</a>", substr.cstr(), substr.cstr());
}
/*-------------------------------------------------
find_include_file - find an include file
-------------------------------------------------*/
static bool find_include_file(astring &srcincpath, int srcrootlen, int dstrootlen, astring &srcfile, astring &dstfile, astring &filename)
{
// iterate over include paths and find the file
for (include_path *curpath = incpaths; curpath != NULL; curpath = curpath->next)
{
// a '.' include path is specially treated
if (curpath->path == ".")
srcincpath.cpysubstr(srcfile, 0, srcfile.rchr(0, PATH_SEPARATOR[0]));
else
srcincpath.cpysubstr(srcfile, 0, srcrootlen + 1).cat(curpath->path);
// append the filename piecemeal to account for directories
int lastsepindex = 0;
int sepindex;
while ((sepindex = filename.chr(lastsepindex, '/')) != -1)
{
// handle .. by removing a chunk from the incpath
astring pathpart(filename, lastsepindex, sepindex - lastsepindex);
if (pathpart == "..")
{
sepindex = srcincpath.rchr(0, PATH_SEPARATOR[0]);
if (sepindex != -1)
srcincpath.substr(0, sepindex);
}
// otherwise, append a path separator and the pathpart
else
srcincpath.cat(PATH_SEPARATOR).cat(pathpart);
// advance past the previous index
lastsepindex = sepindex + 1;
}
// now append the filename
srcincpath.cat(PATH_SEPARATOR).catsubstr(filename, lastsepindex, -1);
// see if we can open it
core_file *testfile;
if (core_fopen(srcincpath, OPEN_FLAG_READ, &testfile) == FILERR_NONE)
{
// close the file
core_fclose(testfile);
// find the longest matching directory substring between the include and source file
lastsepindex = 0;
while ((sepindex = srcincpath.chr(lastsepindex, PATH_SEPARATOR[0])) != -1)
{
// get substrings up to the current directory
astring tempfile(srcfile, 0, sepindex);
astring tempinc(srcincpath, 0, sepindex);
// if we don't match, stop
if (tempfile != tempinc)
break;
lastsepindex = sepindex + 1;
}
// chop off the common parts of the paths
astring tempfile(srcfile, lastsepindex, -1);
srcincpath.substr(lastsepindex, -1).replacechr(PATH_SEPARATOR[0], '/');
// for each directory left in the filename, we need to prepend a "../"
while ((sepindex = tempfile.chr(0, PATH_SEPARATOR[0])) != -1)
{
tempfile.substr(sepindex + 1, -1);
srcincpath.ins(0, "../");
}
srcincpath.cat(".html");
// free the strings and return the include path
return true;
}
}
return false;
}