summaryrefslogblamecommitdiffstatshomepage
path: root/src/tools/src2html.cpp
blob: 32a21de065e5c853c4d9d0e7409cd8af7c47d794 (plain) (tree)
1
2
3
4
5
6
7
8

                                



                                                                            

                                                                            





                                   
 





                                                                            
                   
                    
                    






                                                                            


                                     






                                                                            
              






                          

 
                  
 

                                  


  
                  
 

                              


  
                   
 
                             
                             


  
                 
 
                             
                             


  





                                                                            




                                             
                                


                                       
                                  





                                              
                                  




                                          













                                      
                                  

                                   
                                   
                                  
                                   
                                   
                                        

                                      
                                    

                                    
                                          

                                  

                                      
                                    
                                   

                                   
                                    

                                  
                                    

                                  






                                       
                                      
                                              




                                    
                                         

                                    




                                      
                                     

                                      

                                      
                                     

                                      
                                     
                                   

  
                               







                                 
                                     
  
                                  







                                                                            
                        

                                                                                                                                                                                        
 
               

                                                                                                                               
 
               
                                                                                        
                                                                                                                     
                                                                                                                                                          










                                                                            





                                                                                                                  

                                
                              
                                               
                                                                         

                                                     


                                         
                                

                                                   
                                                        
                                                    
                         
                                                               
                                                                                                            


                                                                    
 
                                  

                                                         
                                           



                                                         
                                           

                                    

                                                         
                                                 

                                    
                    
                                       

         
                                        
                                                                                       
                               
 
                                                     
                         
                     
                                                                                                   
         
                                                                 
                             
         
 
                              
                                     



                                                                  
                                                       




                                                                                 

                                                            
 
                                      
                                                                                                     







                                                                            



                                                               
                                                  


 



                                                   
                                                                                                                                                  
 
                                                                                                                                                        
 
                                       
                                   
                                                                   
 
                               
                              
                                                                                             
                                                                                                              
 
                                          


                                                                      
 


                                                                                            
         
                                                     
 
                                                            
                                                                
                                   

                                   
                              

                 
                                             
                                                   
                              
                                           
                                                        

                                                                               
                                                             
                                                                 



                                                    
 
                                      
                            
 
                                        

                                 
 
                                              
                                                         
                          
                                                                                             

                                                     
                                

                                                                                    
                                   
                               




                                                      
                                   
 
                                            
                                                                                                            
                 
                                       
                                            
                                                                                                                                                               
 
                                                    
                                            
                                                                                                                    
 
                                                       
                                            
                                                                                  
                         
                                                           
                                                                   
                                                                    
                                                                                                   
                                         
                                                                 
                                                      
                                         
 
                                                                     
                                                              
                                 
                                                                                                                                         
                                                                 
                                                                                                                                                           
                                                                                                                                                                  
                                 
                         
 
                                                          

                            
                                                                                                                            
                                                         
                                                                                                                                                          
                                                                                                                       
                         

                 
                                                        
                                    
                                                       
 
                                                 
                                       

                                                      
                                    

                                    

         
                                 
                                                                                               








                                                   
                                                                                                                                                                                       
 
                                       
                                    

                                                                     
                                                                    












                                                                                                     


                                 
                                            
                                                                 
                                                             




                                                                   
                                            



                                                                  
                                            










                                                      

                                      
                                              
                                                                
                                                              
 
                               
                                 
                                                                                         
         
                                                                               


                         
                               
                                                                                                       
                           
         
                                                                                


                         
                                          


                                                                         
 
                               
                                                  
 



                                                
                                   
                                                                    
         
                                             
                                
                                                                                                          







                                                             
                                     
                               
                                                            
                 
                                               
 
                                                                                           



                                                                                                                                            
                                                                                                                      


                                                                            
                                                          


                                                                                                                                          
                                                                                                   


                                                                          
                                                           


                                 
                                                                                           

                                                                                                                                                                           
                                                                                                               


                                                                     
                                                         

                         
                                                                                          



                                                                                                               
                                              
 
                                                            
                                                                              


                                                                

                                                               
                                                                                                    

                                                                                                                                         
                                                                                                                                               



                                                                                      
                                                                                    
                                                                                                                    
                                                                                      




                                                      
                                                     

                                       
                                                          


                                                            
                                                         



                                                 
                                                               

                                                                     
                                                       


                                                                                                                 
                                                                                                              
                                            
                                                                

                                                         
 
                                                          


                                                                                    
                                                                        
                                                 

                                                                                                        
                                                                                                                                          
                                                         
                                                                                                                                
                                                                                         
                                                         



                                                 
                                                       
                                                                                                                       

                                                              
                                                                  
                                                         
                                                                                              
                                            
                                                                

                                                          
                                                                  

                                 
                                                                         
                                                   
                                                           
                                                   
                                                          
                                                   
                                                          
                                    
                                                        

                                         
 
                                              
                                      
                                                                                               

                 
                                         

                                      
                                             
                                                  

                 
                                             
                                  
                                                 

         
                     
                                  
 
                         
                                                                                  













                                                                            
                                                                                                                              
 
                               
                                  
                                                                                                                                                           
                                              
 
                         

                                                          
                                                         
 
                          








                                                   
                                                                                                                   
 

                                                          

                                                         












                                                                            
                                                                                       
 

                                                                                  







                                                   
                                                                                                                    
 

                                      
                                                                                                                              



                             
                                    
                                  
                                                      

                                                      
 


                                                     
                                                                                                                                    
         
                                                                            

                           
                                          
                                                                     

                                                                    



                                           
                                                    
                                                
                             
                                                                             
                              
                                                                                     
            
                                                                                          






                                                   
                                                                                                                                                         
 
                                                       
                                                                                           
         
                                                          

                                                                                                      
                    
                                                                                                   

                                                                           

                                     
                                                                                    
                 
                                                                         

                                                                                              
                         
                                                                                      
                                                   
                                                                   

                         
                                                                              
                            
                                                                                   
 
                                                          
                                                    

                 
                                          
                                                                                            
 
                                        
                                              
                                                                                                         
                 
                                         
                                         
 
                                                                                                            
                                         
                                                                                                            
                         
                                                                             

                                                                             
 
                                                          
                                                                 



                                                            
                                                                 


                                                                          
 
                                                                                              
                                                                                            
                         
                                                                
                                                            
                         
                                                   
 

                                                                       
                 
         
                     
 
// 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 <assert.h>
#include "osdcore.h"
#include "corestr.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;
	std::string     path;
};


struct list_entry
{
	list_entry *    next;
	std::string     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[] =
{
	{ nullptr, 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_t", CUSTOM_CLASS },
    { "uint8_t", CUSTOM_CLASS },
    { "int16_t", CUSTOM_CLASS },
    { "uint16_t", CUSTOM_CLASS },
    { "int32_t", CUSTOM_CLASS },
    { "uint32_t", CUSTOM_CLASS },
    { "int64_t", CUSTOM_CLASS },
    { "uint64_t", CUSTOM_CLASS },
    { "ARRAY_LENGTH", CUSTOM_CLASS },
*/
	{ nullptr, KEYWORD_CLASS }
};



/***************************************************************************
    PROTOTYPES
***************************************************************************/

// core output functions
static int recurse_dir(int srcrootlen, int dstrootlen, std::string &srcdir, std::string &dstdir, std::string &tempheader, std::string &tempfooter);
static int output_file(file_type type, int srcrootlen, int dstrootlen, std::string &srcfile, std::string &dstfile, bool link_to_file, std::string &tempheader, std::string &tempfooter);

// HTML helpers
static util::core_file::ptr create_file_and_output_header(std::string &filename, std::string &templatefile, std::string &path);
static void output_footer_and_close_file(util::core_file::ptr &&file, std::string &templatefile, std::string &path);

// path helpers
static std::string &normalized_subpath(std::string &dest, std::string &path, int start);
static void output_path_as_links(util::core_file &file, std::string &path, bool end_is_directory, bool link_to_file);
static bool find_include_file(std::string &srcincpath, int srcrootlen, int dstrootlen, std::string &srcfile, std::string &dstfile, std::string &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;
	std::string 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 != nullptr)
			{
				(*incpathhead)->next = nullptr;
				strreplacechr((*incpathhead)->path.assign(&arg[2]), '/', PATH_SEPARATOR[0]);
				incpathhead = &(*incpathhead)->next;
			}
		}

		// other parameter
		else if (arg[0] != '-' && unadorned == 0)
		{
			srcdir.assign(arg);
			unadorned++;
		}
		else if (arg[0] != '-' && unadorned == 1)
		{
			dstdir.assign(arg);
			unadorned++;
		}
		else if (arg[0] != '-' && unadorned == 2)
		{
			tempfilename.assign(arg);
			unadorned++;
		}
		else
			usage(argv[0]);
	}

	// make sure we got 3 parameters
	if (srcdir.length() == 0 || dstdir.length() == 0 || tempfilename.length() == 0)
		usage(argv[0]);

	// read the template file into an std::string
	uint32_t bufsize;
	void *buffer;
	if (util::core_file::load(tempfilename.c_str(), &buffer, bufsize) == osd_file::error::NONE)
	{
		tempheader.assign((const char *)buffer, bufsize);
		free(buffer);
	}

	// verify the template
	if (tempheader.length() == 0)
	{
		fprintf(stderr, "Unable to read template file\n");
		return 1;
	}
	int result = tempheader.find("<!--CONTENT-->");
	if (result == -1)
	{
		fprintf(stderr, "Template is missing a <!--CONTENT--> marker\n");
		return 1;
	}
	tempfooter.assign(tempheader).erase(0, result + 14);
	tempheader.erase(result);

	// recurse over subdirectories
	return recurse_dir(srcdir.length(), dstdir.length(), 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.compare(entry2->name);
}


/*-------------------------------------------------
    recurse_dir - recurse through a directory
-------------------------------------------------*/

static int recurse_dir(int srcrootlen, int dstrootlen, std::string &srcdir, std::string &dstdir, std::string &tempheader, std::string &tempfooter)
{
	static const osd::directory::entry::entry_type typelist[] = { osd::directory::entry::entry_type::DIR, osd::directory::entry::entry_type::FILE };

	// extract a normalized subpath
	std::string srcdir_subpath;
	normalized_subpath(srcdir_subpath, srcdir, srcrootlen + 1);

	// create an index file
	std::string indexname;
	indexname = string_format("%s%c%s", dstdir.c_str(), PATH_SEPARATOR[0], "index.html");
	util::core_file::ptr indexfile = create_file_and_output_header(indexname, tempheader, srcdir_subpath);

	// output the directory navigation
	indexfile->printf("<h3>Viewing Directory: ");
	output_path_as_links(*indexfile, srcdir_subpath, true, false);
	indexfile->printf("</h3>");

	// iterate first over directories, then over files
	int result = 0;
	for (int entindex = 0; entindex < ARRAY_LENGTH(typelist) && result == 0; entindex++)
	{
		auto entry_type = typelist[entindex];

		// open the directory and iterate through it
		auto dir = osd::directory::open(srcdir.c_str());
		if (dir == nullptr)
		{
			result = 1;
			break;
		}

		// build up the list of files
		const osd::directory::entry *entry;
		int found = 0;
		list_entry *list = nullptr;
		while ((entry = dir->read()) != nullptr)
			if (entry->type == entry_type && entry->name[0] != '.')
			{
				auto lentry = new list_entry;
				lentry->name.assign(entry->name);
				lentry->next = list;
				list = lentry;
				found++;
			}

		// close the directory
		dir.reset();

		// skip if nothing found
		if (found == 0)
			continue;

		// allocate memory for sorting
		auto listarray = new list_entry *[found];
		found = 0;
		for (list_entry *curlist = list; curlist != nullptr; curlist = curlist->next)
			listarray[found++] = curlist;

		// sort the list
		qsort(listarray, found, sizeof(listarray[0]), compare_list_entries);

		// rebuild the list
		list = nullptr;
		while (--found >= 0)
		{
			listarray[found]->next = list;
			list = listarray[found];
		}
		delete[] listarray;

		// iterate through each file
		for (list_entry *curlist = list; curlist != nullptr && result == 0; curlist = curlist->next)
		{
			// add a header
			if (curlist == list)
				indexfile->printf("\t<h2>%s</h2>\n\t<ul>\n", (entry_type == osd::directory::entry::entry_type::DIR) ? "Directories" : "Files");

			// build the source filename
			std::string srcfile;
			srcfile = string_format("%s%c%s", srcdir.c_str(), PATH_SEPARATOR[0], curlist->name.c_str());

			// if we have a file, output it
			std::string dstfile;
			if (entry_type == osd::directory::entry::entry_type::FILE)
			{
				// make sure we care, first
				file_type type = FILE_TYPE_INVALID;
				for (auto & elem : extension_lookup)
					if (core_filename_ends_with(curlist->name, elem.extension))
					{
						type = elem.type;
						break;
					}

				// if we got a valid file, process it
				if (type != FILE_TYPE_INVALID)
				{
					dstfile = string_format("%s%c%s.html", dstdir.c_str(), PATH_SEPARATOR[0], curlist->name.c_str());
					if (indexfile != nullptr)
						indexfile->printf("\t<li><a href=\"%s.html\">%s</a></li>\n", curlist->name.c_str(), curlist->name.c_str());
					result = output_file(type, srcrootlen, dstrootlen, srcfile, dstfile, srcdir.compare(dstdir) == 0, tempheader, tempfooter);
				}
			}

			// if we have a directory, recurse
			else
			{
				dstfile = string_format("%s%c%s", dstdir.c_str(), PATH_SEPARATOR[0], curlist->name.c_str());
				if (indexfile != nullptr)
					indexfile->printf("\t<li><a href=\"%s/index.html\">%s/</a></li>\n", curlist->name.c_str(), curlist->name.c_str());
				result = recurse_dir(srcrootlen, dstrootlen, srcfile, dstfile, tempheader, tempfooter);
			}
		}

		// close the list if we found some stuff
		if (list != nullptr)
			indexfile->printf("\t</ul>\n");

		// free all the allocated entries
		while (list != nullptr)
		{
			list_entry *next = list->next;
			delete list;
			list = next;
		}
	}

	if (indexfile != nullptr)
		output_footer_and_close_file(std::move(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, std::string &srcfile, std::string &dstfile, bool link_to_file, std::string &tempheader, std::string &tempfooter)
{
	// extract a normalized subpath
	std::string srcfile_subpath;
	normalized_subpath(srcfile_subpath, srcfile, srcrootlen + 1);

	fprintf(stderr, "Processing %s\n", srcfile_subpath.c_str());

	// 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_t)token_chars[toknum]] = true;

	// open the source file
	util::core_file::ptr src;
	if (util::core_file::open(srcfile, OPEN_FLAG_READ, src) != osd_file::error::NONE)
	{
		fprintf(stderr, "Unable to read file '%s'\n", srcfile.c_str());
		return 1;
	}

	// open the output file
	util::core_file::ptr dst = create_file_and_output_header(dstfile, tempheader, srcfile_subpath);
	if (dst == nullptr)
	{
		fprintf(stderr, "Unable to write file '%s'\n", dstfile.c_str());
		return 1;
	}

	// output the directory navigation
	dst->printf("<h3>Viewing File: ");
	output_path_as_links(*dst, srcfile_subpath, false, link_to_file);
	dst->printf("</h3>");

	// start with some tags
	dst->printf("\t<pre class=\"source\">\n");

	// iterate over lines in the source file
	int linenum = 1;
	bool in_comment = false;
	char srcline[4096];
	std::ostringstream dstline;
	while (src->gets(srcline, ARRAY_LENGTH(srcline)) != nullptr)
	{
		// start with the line number
		dstline.str("");
		util::stream_format(dstline, "<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_t curquote = 0;
		int curcol = 0;
		for (char *srcptr = srcline; *srcptr != 0; )
		{
			uint8_t 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)
				{
					util::stream_format(dstline, "<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)
				{
					util::stream_format(dstline, "%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)
			{
				util::stream_format(dstline, "<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_t)*temp])
					temp++;
				toklength = temp - (srcptr - 1);

				// scan the token table
				last_token_was_include = false;
				for (curtoken = token_table; curtoken->token != nullptr; curtoken++)
					if (strncmp(srcptr - 1, curtoken->token, toklength) == 0 && strlen(curtoken->token) == toklength)
					{
						util::stream_format(dstline, "<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.put(' ');
					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)
						util::stream_format(dstline, "<span class=\"string\">%c", ch);
					else
						dstline.put(ch);
					in_quotes = true;
					curquote = ch;

					// handle includes
					if (last_token_was_include)
					{
						char *endquote = strchr(srcptr, ch);
						if (endquote != nullptr)
						{
							std::string filename(srcptr, endquote - srcptr);
							std::string target;
							if (find_include_file(target, srcrootlen, dstrootlen, srcfile, dstfile, filename))
							{
								util::stream_format(dstline, "<a href=\"%s\">", target.c_str());
								quotes_are_linked = true;
							}
						}
					}
				}

				// track closing quotes
				else if (!in_comment && !in_inline_comment && in_quotes && (ch == curquote) && !escape)
				{
					if (quotes_are_linked)
						dstline << "</a>";
					if (color_quotes)
						util::stream_format(dstline, "%c</span>", ch);
					else
						dstline.put(ch);
					in_quotes = false;
					curquote = 0;
					quotes_are_linked = false;
				}

				// else just output the current character
				else if (ch == '&')
					dstline << "&amp;";
				else if (ch == '<')
					dstline << "&lt;";
				else if (ch == '>')
					dstline << "&gt;";
				else
					dstline.put(ch);
				curcol++;
			}

			// Update escape state
			if (in_quotes)
				escape = (ch == '\\' && type == FILE_TYPE_C) ? !escape : false;
		}

		// finish inline comments
		if (in_inline_comment)
		{
			dstline << "</span>";
			in_inline_comment = false;
		}

		// append a break and move on
		dstline.put('\n');
		dst->puts(dstline.str().c_str());
	}

	// close tags
	dst->printf("\t</pre>\n");

	// close the file
	output_footer_and_close_file(std::move(dst), tempfooter, srcfile_subpath);
	return 0;
}



/***************************************************************************
    HTML OUTPUT HELPERS
***************************************************************************/

/*-------------------------------------------------
    create_file_and_output_header - create a new
    HTML file with a standard header
-------------------------------------------------*/

static util::core_file::ptr create_file_and_output_header(std::string &filename, std::string &templatefile, std::string &path)
{
	// create the indexfile
	util::core_file::ptr file;
	if (util::core_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS | OPEN_FLAG_NO_BOM, file) != osd_file::error::NONE)
		return util::core_file::ptr();

	// print a header
	std::string modified(templatefile);
	strreplace(modified, "<!--PATH-->", path.c_str());
	file->write(modified.c_str(), modified.length());

	// 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(util::core_file::ptr &&file, std::string &templatefile, std::string &path)
{
	std::string modified(templatefile);
	strreplace(modified, "<!--PATH-->", path.c_str());
	file->write(modified.c_str(), modified.length());
	file.reset();
}



/***************************************************************************
    HTML OUTPUT HELPERS
***************************************************************************/

/*-------------------------------------------------
    normalized_subpath - normalize a path to
    forward slashes and extract a subpath
-------------------------------------------------*/

static std::string &normalized_subpath(std::string &dest, std::string &path, int start)
{
	strreplacechr(dest.assign(path.substr(start, -1)),PATH_SEPARATOR[0], '/');
	return dest;
}


/*-------------------------------------------------
    output_path_as_links - output a path as a
    series of links
-------------------------------------------------*/

static void output_path_as_links(util::core_file &file, std::string &path, bool end_is_directory, bool link_to_file)
{
	// first count how deep we are
	int srcdepth = 0;
	for (int slashindex = path.find_first_of('/'); slashindex != -1; slashindex = path.find_first_of('/', slashindex + 1))
		srcdepth++;
	if (end_is_directory)
		srcdepth++;

	// output a link to the root
	file.printf("<a href=\"");
	for (int depth = 0; depth < srcdepth; depth++)
		file.printf("../");
	file.printf("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.find_first_of('/', lastslash); slashindex != -1; slashindex = path.find_first_of('/', lastslash))
	{
		std::string substr(path, lastslash, slashindex - lastslash);

		curdepth++;
		file.printf("<a href=\"");
		for (int depth = curdepth; depth < srcdepth; depth++)
			file.printf("../");
		file.printf("index.html\">%s</a>/", substr.c_str());

		lastslash = slashindex + 1;
	}

	// and a final link to the current directory
	std::string substr(path, lastslash, -1);
	if (end_is_directory)
		file.printf("<a href=\"index.html\">%s</a>", substr.c_str());
	else if (link_to_file)
		file.printf("<a href=\"%s\">%s</a>", substr.c_str(), substr.c_str());
	else
		file.printf("<a href=\"%s.html\">%s</a>", substr.c_str(), substr.c_str());
}


/*-------------------------------------------------
    find_include_file - find an include file
-------------------------------------------------*/

static bool find_include_file(std::string &srcincpath, int srcrootlen, int dstrootlen, std::string &srcfile, std::string &dstfile, std::string &filename)
{
	// iterate over include paths and find the file
	for (include_path *curpath = incpaths; curpath != nullptr; curpath = curpath->next)
	{
		// a '.' include path is specially treated
		if (curpath->path.compare(".") == 0)
			srcincpath.assign(srcfile.substr(0, srcfile.find_last_of(PATH_SEPARATOR[0])));
		else
			srcincpath.assign(srcfile.substr(0, srcrootlen + 1)).append(curpath->path);

		// append the filename piecemeal to account for directories
		int lastsepindex = 0;
		int sepindex;
		while ((sepindex = filename.find_first_of('/', lastsepindex)) != -1)
		{
			// handle .. by removing a chunk from the incpath
			std::string pathpart(filename, lastsepindex, sepindex - lastsepindex);
			if (pathpart.compare("..")==0)
			{
				sepindex = srcincpath.find_last_of(PATH_SEPARATOR[0]);
				if (sepindex != -1)
					srcincpath.erase(sepindex);
			}

			// otherwise, append a path separator and the pathpart
			else
				srcincpath.append(PATH_SEPARATOR).append(pathpart);

			// advance past the previous index
			lastsepindex = sepindex + 1;
		}

		// now append the filename
		srcincpath.append(PATH_SEPARATOR).append(filename.substr(lastsepindex, -1));

		// see if we can open it
		util::core_file::ptr testfile;
		if (util::core_file::open(srcincpath, OPEN_FLAG_READ, testfile) == osd_file::error::NONE)
		{
			// close the file
			testfile.reset();

			// find the longest matching directory substring between the include and source file
			lastsepindex = 0;
			while ((sepindex = srcincpath.find_first_of(PATH_SEPARATOR[0], lastsepindex)) != -1)
			{
				// get substrings up to the current directory
				std::string tempfile(srcfile, 0, sepindex);
				std::string tempinc(srcincpath, 0, sepindex);

				// if we don't match, stop
				if (tempfile.compare(tempinc)!=0)
					break;
				lastsepindex = sepindex + 1;
			}

			// chop off the common parts of the paths
			std::string tempfile(srcfile, lastsepindex, -1);
			srcincpath = srcincpath.substr(lastsepindex, -1);
			strreplacechr(srcincpath, PATH_SEPARATOR[0], '/');

			// for each directory left in the filename, we need to prepend a "../"
			while ((sepindex = tempfile.find_first_of(PATH_SEPARATOR[0])) != -1)
			{
				tempfile.erase(0, sepindex + 1);
				srcincpath.insert(0, "../");
			}
			srcincpath.append(".html");

			// free the strings and return the include path
			return true;
		}
	}
	return false;
}