/*************************************************************************** MAME source code to HTML converter **************************************************************************** Copyright Aaron Giles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name 'MAME' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************** Template file format: = insert path = insert content ***************************************************************************/ #include #include #include #include #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 [-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, ""); if (result == -1) { fprintf(stderr, "Template is missing a 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, "

Viewing Directory: "); output_path_as_links(indexfile, srcdir_subpath, true, false); core_fprintf(indexfile, "

"); // 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

%s

\n\t
    \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
  • %s
  • \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
  • %s/
  • \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
\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_end_esc = "-->"; 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, "

Viewing File: "); output_path_as_links(dst, srcfile_subpath, false, link_to_file); core_fprintf(dst, "

"); // start with some tags core_fprintf(dst, "\t
\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("%5d  ", 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("%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", 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("%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("%s", 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("%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("", 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("");
					if (color_quotes)
						dstline.catprintf("%c", 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("&");
				else if (ch == '<')
					dstline.catprintf("<");
				else if (ch == '>')
					dstline.catprintf(">");
				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("");
			in_inline_comment = false;
		}

		// append a break and move on
		dstline.catprintf("\n");
		core_fputs(dst, dstline);
	}

	// close tags
	core_fprintf(dst, "\t
\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.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.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, "<root>/"); // 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, "%s/", 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, "%s", substr.cstr()); else if (link_to_file) core_fprintf(file, "%s", substr.cstr(), substr.cstr()); else core_fprintf(file, "%s", 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; }