1: /* webcpp - engine.cpp
     2:  * Copyright (C)2001-2003 Jeffrey Bakker
     3: 
     4:  * This program is free software; you can redistribute it and/or modify
     5:  * it under the terms of the GNU General Public License as published by
     6:  * the Free Software Foundation; either version 2 of the License, or
     7:  * (at your option) any later version.
     8: 
     9:  * This program is distributed in the hope that it will be useful,
    10:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12:  * GNU General Public License for more details.
    13: 
    14:  * You should have received a copy of the GNU General Public License
    15:  * along with this program; if not, write to the Free Software
    16:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    17:    ___________________________________ .. .
    18:  */
    19: 
    20: //--build=i386-linux --host=i386-linux --target=i386-linux
    21: 
    22: #include <cctype> 
    23: #include <cstdlib> 
    24: #include "engine.h" 
    25: using namespace std;
    26: 
    27: #define DEBUG_DO_PARSING false 
    28: 
    29: #define PARSE_DBL_QUO_STRING parseString(DBL_QUOTES,inDblQuotes) 
    30: #define PARSE_SIN_QUO_STRING parseString(SIN_QUOTES,inSinQuotes) 
    31: #define PARSE_BCK_QUO_STRING parseString(BCK_QUOTES,inBckQuotes) 
    32: 
    33: #define PARSE_A_MARKUP_COMNT parseBigComment("&lt;!-", "--&gt;", inComment) 
    34: #define PARSE_PAS_MOD2_COMNT parseBigComment("(*", "*)", inComment) 
    35: #define PARSE_CLASSICC_COMNT parseBigComment("/*", "*/", inComment) 
    36: #define PARSE_HASKL_98_COMNT parseBigComment("{-", "-", inComment) 
    37: #define PARSE_HTML_TAGS      parseBigComment("&lt;", "&gt;", inHtmTags) 
    38: 
    39: #define PARSE_A_MS_ASP_COMNT parseComment("'") 
    40: #define PARSE_A_ADA_95_COMNT parseComment("--") 
    41: #define PARSE_C_INLINE_COMNT parseComment("//") 
    42: #define PARSE_UNIXHASH_COMNT parseComment("#") 
    43: #define PARSE_ASSEMBLY_COMNT parseComment(";") 
    44: #define PARSE_DBLCOLON_COMNT parseComment("::") 
    45: #define PARSE_REMINDER_COMNT \ 
    46: parseComment("REM"); \
    47: parseComment("rem")
    48: 
    49: #define PARSE_ZEROHASH_COMNT parseCharZeroComment('#') 
    50: #define PARSE_ZFORTRAN_COMNT \ 
    51: parseCharZeroComment('C'); \
    52: parseCharZeroComment('c'); \
    53: parseCharZeroComment('*'); \
    54: parseCharZeroComment('!')
    55: 
    56: #define PARSE_ASSEMBLY_MACRO parseVariable("%") 
    57: #define PARSE_SCALAR_VARIABL parseVariable("$") 
    58: #define PARSE_ARRAYS_VARIABL parseVariable("@") 
    59: #define PARSE_HASHED_VARIABL parseVariable("%") 
    60: 
    61: #define PRE_PARSE_CODE       pre_parse() 
    62: #define PARSE_PREPROCESSOR   parsePreProc() 
    63: #define PARSE_LABELS         parseLabel() 
    64: #define PARSE_KEYWORDS       parseKeys() 
    65: #define PARSE_NUMBERS        parseNum() 
    66: 
    67: 
    68: // initialize data members ----------------------------------------------------
    69: void Engine::init_switches() {
    70: 
    71: 	// command line options
    72: 	opt_bigtab  = false;
    73: 	opt_webcpp  = false;
    74: 	opt_hypinc  = false;
    75: 	opt_follow  = false;
    76: 	opt_number  = false;
    77: 	opt_extcss  = false;
    78: 	opt_anchor  = false;
    79: 
    80: 	// abort switches
    81: 	inHtmTags   = false;
    82: 	inDblQuotes = false;
    83: 	inSinQuotes = false;
    84: 	inBckQuotes = false;
    85: 	inComment   = false;
    86: 	endComment  = false;
    87: 
    88: 	// common language
    89: 	doStrings   = Yes;
    90: 	doNumbers   = Yes;
    91: 	doKeywords  = Yes;
    92: 	doCaseKeys  = Yes;
    93: 	doSymbols   = No;
    94: 	doLabels    = No;
    95: 	doPreProc   = No;
    96: 	doScalars   = No;
    97: 	doArrays    = No;
    98: 	doHashes    = No;
    99: 	doHtmlTags  = No;
   100: 	doHtmComnt  = No;
   101: 	doHskComnt  = No;
   102: 	doPasComnt  = No;
   103: 	doBigComnt  = No;
   104: 	doCinComnt  = No;
   105: 	doUnxComnt  = No;
   106: 	doAsmComnt  = No;
   107: 	doRemComnt  = No;
   108: 	doAdaComnt  = No;
   109: 	doFtnComnt  = No;
   110: 	doTclComnt  = No;
   111: 	doAspComnt  = No;
   112: 	doBatComnt  = No;
   113: 
   114: 	lncount  = 1;
   115: 	tabwidth = 8;
   116: 	tw = "8";
   117: 	
   118: 	IO.init_switches();
   119: }
   120: // set the width of the tabs --------------------------------------------------
   121: void Engine::setTabWidth(string width) {
   122: 
   123: 	tabwidth = atoi(width.data());
   124: 	if(tabwidth == 0) {
   125:         	tabwidth = 8;
   126: 		tw = "8";
   127: 	}
   128: 	tw = width;
   129: }
   130: // format the plain text for proper display in HTML ---------------------------
   131: void Engine::pre_parse() {
   132: 
   133: 	// the virtual Nth character factoring in for escapes
   134: 	int i_esc = 0;
   135: 
   136: 	for (int i=0; i < (int)buffer.size(); i++) {
   137: 
   138: 		// escape from HTML escapes
   139: 		if (buffer[i] == '&') {buffer.replace(i,1,"&amp;");i_esc+=4;}
   140: 		else
   141: 		if (buffer[i] == '<') {buffer.replace(i,1, "&lt;");i_esc+=3;}
   142: 		else
   143: 		if (buffer[i] == '>') {buffer.replace(i,1, "&gt;");i_esc+=3;}
   144: 		// escape from accidental HTML tags
   145: 
   146: 		if (opt_bigtab)
   147: 		// convert tabs into spaces
   148: 		if (buffer[i] == '\t') {
   149: 
   150: 			int j;
   151: 			buffer.erase(i,1);
   152: 
   153: 			// factor in the number of escapes
   154: 			if((i-i_esc) % tabwidth == 0) {
   155: 
   156: 				for(j=0; j < tabwidth; j++) {
   157: 
   158: 					buffer.insert(i," ");
   159: 				}
   160: 				i+=tabwidth-1;
   161: 			}else{
   162: 				int spaces = tabwidth - ((i-i_esc) % tabwidth);
   163: 
   164: 				for(j=0; j < spaces; j++) {
   165: 
   166: 					buffer.insert(i," ");
   167: 				}
   168: 				i+=spaces-1;
   169: 			}
   170: 		}
   171: 		// for browsers that don't display tabs properly
   172: 	}
   173: }
   174: // erases tags (use for inside of inline comments) ----------------------------
   175: void Engine::eraseTags(int start, int fin) {
   176: 
   177: 	if(fin ==  0) {fin = buffer.size();}
   178: 	if(fin == -1) {fin = buffer.size();}
   179: 
   180: 	int erase1, erase2;
   181: 	int offset1, offset2;
   182: 	string srchstr;
   183: 
   184: 	srchstr = "<font CLASS=";
   185: 	offset1 = 20;
   186: 	offset2 = 7;
   187: 
   188: 	// erase all the colours previously made
   189: 	while(buffer.find(srchstr,start) != -1 &&
   190: 	      buffer.find(srchstr,start) < fin) {
   191: 
   192: 		//erasing opening font tags
   193: 		erase1 = buffer.find(srchstr,start);
   194: 		if(erase1 != -1 && erase1 < fin) {
   195: 			buffer.erase(erase1,offset1);
   196: 		}
   197: 		//erasing closing font tags
   198: 		erase2 = buffer.find("</font>",start);
   199: 		if(erase2 != -1 && erase2 < fin) {
   200: 			buffer.erase(erase2,offset2);
   201: 		}
   202: 	}
   203: 	inDblQuotes = false;
   204: 	inSinQuotes = false;
   205: 	inBckQuotes = false;
   206: }
   207: // anchors the line (for hyperlinking capabilities) ---------------------------
   208: void Engine::makeAnchor() {
   209: 
   210: 	IO << "<a name=\"line" << lncount << "\"/>";
   211: }
   212: // prints the line number in the margin ---------------------------------------
   213: void Engine::makeMargin() {
   214: 
   215: 	string space = "";
   216: 
   217: 	// setting margin alignment
   218: 	if(lncount < 100000)	{space += " ";}
   219: 	if(lncount < 10000)	{space += " ";}
   220: 	if(lncount < 1000)	{space += " ";}
   221: 	if(lncount < 100)	{space += " ";}
   222: 	if(lncount < 10)	{space += " ";}
   223: 
   224: 	IO	<< space	<< "<font CLASS=comment>"
   225: 		<< lncount	<< ":</font> ";
   226: }
   227: //-----------------------------------------------------------------------------
   228: // check if parsing needs to be aborted ---------------------------------------
   229: bool Engine::abortParse() {
   230: 
   231: //	if(doHtmlTags && inHtmTags)
   232: //			{return true;}
   233: 
   234: 	if(endComment)	{return true;}
   235: 	if(inDblQuotes)	{return true;}
   236: 	if(!doAspComnt)	{
   237: 	if(inSinQuotes)	{return true;}
   238: 	}
   239: 	if(inBckQuotes)	{return true;}
   240: 	if(inComment)	{return true;}
   241: 
   242: 	return false;
   243: }
   244: // check if colouring needs to be aborted -------------------------------------
   245: bool Engine::abortColour(int index) {
   246: 
   247: 	if(doHtmComnt
   248: 	&& ( isInsideIt(index,"&lt;","&gt;")
   249: 	&&   isInsideIt(index,"&gt;","&lt;") ))	{return true;}
   250: 	if(isInsideIt(index,"/*","*/"))		{return true;}
   251: 	if(isInsideIt(index,"(*","*)"))		{return true;}
   252: 	if(isInsideIt(index,"&lt;!","&gt;"))	{return true;}
   253: 	if(isInsideIt(index,"\"","\""))		{return true;}
   254: 	if(!doAspComnt) {
   255: 		if(isInsideIt(index,"'","'"))	{return true;}
   256: 	}
   257: 	if(isInsideIt(index,"`","`"))		{return true;}
   258: 
   259: 	return false;
   260: }
   261: // check if the index is inside the specified boundaries ----------------------
   262: bool Engine::isInsideIt(int index, string start, string end) {
   263: 
   264: 	// count the number of starts and ends
   265: 	// and return true for an odd number
   266: 	
   267: 	if(buffer.find(start,0) == -1) {return false;}
   268: 
   269: 	int l = 0;
   270: 	int r = 0;
   271: 	int idx;
   272: 
   273: 	idx = buffer.find(end,index);
   274: 	while(idx < buffer.size()) { // idx < string::npos && idx != -1
   275: 		if(idx != -1 && buffer[idx-1] != '\\'){r++;}
   276: 		idx = buffer.find(end,idx+1);
   277: 	}
   278: 
   279: 	idx = buffer.rfind(start,index);
   280: 	while(idx > 0) {
   281: 		if(idx != -1 && buffer[idx-1] != '\\'){l++;}
   282: 		idx = buffer.rfind(start,idx-1);
   283: 	}
   284: 
   285: 	if(r % 2 == 1 && l % 2 == 1) {return true;}
   286: 
   287: 	return false;
   288: }
   289: // for HTML highlighting ------------------------------------------------------
   290: bool Engine::isInsideTag(int index) {
   291: 
   292: 	return false;
   293: }
   294: // number accuracy ------------------------------------------------------------
   295: bool Engine::isNotWord(int index) {
   296: 
   297: 	if(isalpha(buffer[index+1]))		{return false;}
   298: 
   299: 	while(index > 0) {
   300: 
   301: 		if(isalpha(buffer[index]))	{return false;}
   302: 		if(buffer[index] == '_')	{return false;}
   303: 		if(buffer[index] == '#')	{return false;}
   304: 
   305: 		if(ispunct(buffer[index]) ||
   306: 		   isspace(buffer[index]))	{return true;}
   307: 		index--;
   308: 	}
   309: 	return true;
   310: }
   311: // parse for preprocessor directives ------------------------------------------
   312: void Engine::parsePreProc() {
   313: 
   314: 	if(abortParse())     {return;}
   315: 	if(buffer[0] != '#') {return;}
   316: 
   317: 	if(opt_hypinc) {
   318: 		hyperIncludeMe();
   319: 	}
   320: 
   321: 	buffer += " ";
   322: 	buffer.insert(0, "<font CLASS=preproc>");
   323: 
   324: 	int end;
   325: 	for(int i=8;i<buffer.size();i++) {
   326: 		if(isspace(buffer[i])) {end = i;i=buffer.size();}
   327: 	}
   328: 	buffer.insert(end, "</font>");
   329: }
   330: // define symbol characters ---------------------------------------------------
   331: bool Engine::isSymbol(char c) {
   332: 
   333: 	switch(c) {
   334: 
   335: //			case '*':
   336: 			case '!':
   337: 			case '|':
   338: //			case '&':
   339: //			case '<':
   340: //			case '>':
   341: //			case '{':
   342: //			case '}':
   343: //			case '[':
   344: //			case ']':
   345: //			case '(':
   346: //			case ')':
   347: //			case ':':
   348: 			case '-':
   349: 			case '=':
   350: 			case '+': return true;
   351: 			default : return false;
   352: 	}
   353: }
   354: // parse for symbols ----------------------------------------------------------
   355: void Engine::parseSymbol() {
   356: 
   357: 	if(abortParse()) {return;}
   358: 
   359: 	int	end;
   360: 	int	insert = 0;
   361: 
   362: 	for(int i=0; i < buffer.size(); i++) {
   363: 
   364: 		if(isSymbol(buffer[i])) {
   365: 
   366: 			end = i;
   367: 			while(isSymbol(buffer[end+1])) {end++;}
   368: 
   369: 			if(colourSymbol(i,end)) {
   370: 
   371: 				insert += 27;
   372: 				i = end + insert;
   373: 			}
   374: 		}
   375: 	}
   376: // currently enabled for:
   377: // C,C++,Cg,C#,Objective C,Java,Perl,PHP,Python,UScript
   378: }
   379: // colour the symbols ---------------------------------------------------------
   380: bool Engine::colourSymbol(int s, int f) {
   381: 
   382: 	if(abortColour(s)) {return false;}
   383: 	if(!isNotWord(s))  {return false;}
   384: 
   385: 	buffer.insert(s,"<font CLASS=symbols>");
   386: 	buffer.insert(f+21,"</font>");
   387: 
   388: 	return true;
   389: }
   390: // parse labels ---------------------------------------------------------------
   391: void Engine::parseLabel() {
   392: 
   393: 	if(abortParse()) {return;}
   394: 
   395: 	if(buffer.find("/*") != -1) {return;} //prevent comment loop
   396: 	if(buffer.find("(*") != -1) {return;}
   397: 
   398: 
   399: 	int end, beg;
   400: 	
   401: 	end = buffer.size()-1;
   402: 	beg = buffer.rfind(" ",end);
   403: 
   404: 	if(beg == -1) {beg = 0;}
   405: 	if(buffer[end] == ':') {
   406: 		colourLabel(beg,end);
   407: 	}
   408: }
   409: // colourize the labels -------------------------------------------------------
   410: void Engine::colourLabel(int beg, int end) {
   411: 
   412: 	if(abortColour(beg)) {return;}
   413: 	buffer.insert(beg,"<font CLASS=preproc>");
   414: 	buffer.insert(end+21,"</font>");
   415: }
   416: //-----------------------------------------------------------------------------
   417: // parse the buffer for numbers -----------------------------------------------
   418: void Engine::parseNum()
   419: {
   420: 	if(buffer[0] == '#') {return;}
   421: 	if(abortParse())     {return;}
   422: 
   423: 	vector<int>	nums;
   424: 	vector<int>	ends;
   425: 	int  end, insert;
   426: 
   427: 	// grab indexes of all numbers into the vector
   428: 	for(int i=0; i < buffer.size(); i++) {
   429: 
   430: 		if(isdigit(buffer[i]) && !isalpha(buffer[i-1])) {
   431: 			end = i;
   432: 
   433: 			while(isdigit(buffer[end+1]) ||
   434: 				  (buffer[end+1] == '.'  &&
   435: 				  isdigit(buffer[end+2])))
   436: 			{end++;}
   437: 
   438: 			nums.push_back(i);
   439: 			ends.push_back(end);
   440: 			i=end;
   441: 		}
   442: 	}
   443: 	// now colour each number
   444: 	for(int j=0; j < (int)ends.size(); j++) {
   445: 
   446: 		if(j == 0) {insert = 0;}
   447: 		else       {insert += 27;}
   448: 
   449: 		if(!colourNum(nums[j]+insert, ends[j]+insert)) {
   450: 			insert -= 27;
   451: 		}
   452: 	}
   453: 
   454: }
   455: // insert number highlighting tags --------------------------------------------
   456: bool Engine::colourNum(int s, int f) {
   457: 
   458: 	if(abortColour(s)) {return false;}
   459: 	if(!isNotWord(s))  {return false;}
   460: 
   461: 	string cssclass;
   462: 	int fpt;
   463: 
   464: 	fpt = buffer.find(".",s);
   465: 
   466: 	if(fpt != -1 && fpt < f) {
   467: 
   468:  		cssclass = "<font CLASS=floatpt>";
   469: 	} else
   470:         	cssclass = "<font CLASS=integer>";
   471: 
   472: 
   473: 	// insert the font tags
   474: 	buffer.insert(s,	cssclass);
   475: 	buffer.insert(f+21,	"</font>");
   476: 	
   477: 	return true;
   478: }
   479: //-----------------------------------------------------------------------------
   480: // parse the buffer for strings -----------------------------------------------
   481: void Engine::parseString(char quotetype, bool &inside) {
   482: 
   483: 	if(doAdaComnt && !doRemComnt && quotetype == SIN_QUOTES) {return;}
   484: 	if(doAspComnt && quotetype == SIN_QUOTES) {return;}
   485: 
   486: 	string quote, escap1, escap2, cssclass;
   487: 	int index,offset;
   488: 	index = 0;
   489: 
   490: // Support for 3 different string types
   491: 	if       (quotetype == DBL_QUOTES) {	
   492: 		if(inSinQuotes || inBckQuotes) {return;}
   493: 
   494: 		quote  = "\"";
   495: 		
   496: 		// Asp uses single ticks for comments and this
   497: 		// screws up if a double-quoted line is commented out
   498: 		if (!doAspComnt) {
   499: 			escap1 = "'";
   500: 		} else {
   501: 			escap1 = "`";
   502: 		}
   503: 		escap2 = "`";
   504: 
   505: 		cssclass = "dblquot";
   506: 
   507: 	} else if(quotetype == SIN_QUOTES) {
   508: 		if(inDblQuotes || inBckQuotes) {return;}
   509: 
   510: 		quote  = "'";
   511: 		escap1 = "\"";
   512: 		escap2 = "`";
   513: 
   514: 		cssclass = "sinquot";
   515: 
   516: 	} else if(quotetype == BCK_QUOTES) {
   517: 		if(inDblQuotes || inSinQuotes) {return;}
   518: 
   519: 		quote  = "`";
   520: 		escap1 = "\"";
   521: 		escap2 = "'";
   522: 
   523: 		cssclass = "preproc";
   524: 	}
   525: // Double, single, and back quoted ///
   526: 
   527: 
   528: 	index = buffer.find(quote,index);
   529: 	if(index == -1) {return;}
   530: 
   531: 	while (index < string::npos) {
   532: 
   533: 		if(buffer[index -1] == '\\') {
   534: 			if(buffer[index -2] == '\'' && buffer[index +1] == '\'') {
   535: 				index = buffer.find(quote,index+1);
   536: 			}
   537: 		}
   538: 		if(index == -1) {return;}
   539: 
   540: 		while(isInsideIt(index,escap1,escap1)){
   541: 			index = buffer.find(quote,index +1);
   542: 			if(index == -1) {return;}
   543: 		}
   544: 		while(isInsideIt(index,escap2,escap2)){
   545: 			index = buffer.find(quote,index +1);
   546: 			if(index == -1) {return;}
   547: 		}
   548: 
   549: 		while(doHtmComnt && isInsideIt(index,"&gt;","&lt;") ) {
   550: 			index = buffer.find(quote,index +1);
   551: 			if(index == -1) {return;}
   552: 		}
   553: 
   554: 		// keep escape characters in mind
   555: 		while	(buffer[index -1] == '\\' &&
   556: 			(buffer[index -2] != '\\' ||
   557: 			(buffer[index -3] == '\\' &&
   558: 			 buffer[index -4] != '\\'))) {
   559: 
   560: 			index = buffer.find(quote,index +1);
   561: 			if(index == -1) {return;}
   562: 		}
   563: 
   564: 		if(index != -1 	&& !inComment) {
   565: 
   566: 			colourString(index, inside, cssclass);
   567: 		}
   568: 
   569: 		if(inside) {offset = index + 21;}
   570: 		else       {offset = index +  7;}
   571: 
   572: 		index = buffer.find(quote,offset);
   573: 		if(index == -1)          {return;}
   574: 		if(index > buffer.size()){return;}
   575: 	}
   576: }
   577: // insert string highlighting tags --------------------------------------------
   578: void Engine::colourString(int index, bool &inside, string cssclass) {
   579: 
   580: 	if(index > buffer.size()){return;}
   581: 
   582: 	string fntag = "<font CLASS=" + cssclass + ">";
   583: 
   584: 	// open tag
   585: 	if(!inside) {
   586: 		buffer.insert(index,   fntag);
   587: 	} else {
   588: 		buffer.insert(index+1, "</font>");
   589: 	}
   590: 	// or close tag
   591: 	// depending on whether or not inside
   592: 
   593: 	inside = !inside;
   594: }
   595: // parse for multi-line comments ----------------------------------------------
   596: void Engine::parseBigComment(string start, string end, bool &inside) {
   597: 
   598: 	string search, escap, css;
   599: 	int index,offset;
   600: 	bool erase;
   601: 
   602: 	index = 0;
   603: 	erase = true;
   604: 	css = "comment";
   605: 
   606: 	if(inside) {search = end;}
   607: 	else {search = start;}
   608: 	
   609: 	index = buffer.find(search,index);
   610: 	if(index == -1) {return;}
   611: 	if(doCinComnt && start == "/*" && buffer.find("//") < index) {return;}
   612: 	if(doUnxComnt && start == "/*" && buffer.find("#")  < index) {return;}
   613: 
   614: 	if(start == "&lt;" && end == "&gt;" && doHtmlTags) {
   615: 
   616: 		if(buffer.find("&lt;!-") == index || inHtmTags)
   617: 			if(!inside)
   618: 				return;
   619: 		erase = false;
   620:         	css = "preproc";
   621: 	}
   622: 
   623: 	while (index < string::npos) {
   624: 
   625: 		if(inside) {search = end;}
   626: 		else {search = start;}		
   627: 
   628: 		index = buffer.find(search,index);
   629: 		if(index == -1) {return;}
   630: 
   631: 		if(buffer[index -1] == '\\') {
   632: 			if(buffer[index -2] == '\'' && buffer[index +1] == '\'') {
   633: 				index = buffer.find(search,index+1);
   634: 			}
   635: 		}
   636: 		if(index == -1) {return;}
   637: 		if(!isInsideIt(index, "\"", "\"") &&
   638: 		   !isInsideIt(index, "'", "'")   &&
   639: 		   !isInsideIt(index, "`", "`")) {
   640: 			if(inside) {
   641: 				index += end.size()-1;
   642: 		                if(buffer.find(end) == -1) {endComment = true;}
   643: 			}
   644: 			else if(erase)eraseTags(index,0);
   645: 			colourString(index, inside, css);
   646: 		}
   647: 
   648: 		if(inside) {
   649: 			offset = index + 21;
   650: 			search = end;
   651: 		}
   652: 		else {
   653: 			offset = index +  7;
   654: 			search = start;
   655: 		}
   656: 
   657: 		index = buffer.find(search,offset);
   658: 		if(index == -1)          {return;}
   659: 		if(index > buffer.size()){return;}
   660: 	}
   661: }
   662: // parse for keywords ---------------------------------------------------------
   663: void Engine::parseKeys() {
   664: 
   665: 	if(buffer[0] == '#') {return;}
   666: 	if(abortParse())     {return;}
   667: 
   668: 	int i, index, offset = 20;
   669: 	string cmpkey;
   670: 
   671: 	for(i=0; i < (int)keys.size(); i++) {
   672: 
   673: 		cmpkey	= keys[i];
   674: 		index	= noCaseFind(cmpkey,0);
   675: 
   676: 		while(index < buffer.size() && index != -1) {
   677: 
   678: 			if(isKey(index-1, (index) + keys[i].size())) {
   679: 				colourKeys(index, keys[i], "keyword");
   680: 			}
   681: 			index = noCaseFind(cmpkey,(index+cmpkey.size()+offset));
   682: 		}
   683: 	}
   684: 
   685: 	for(i=0; i < (int)types.size(); i++) {
   686: 
   687: 		cmpkey	= types[i];
   688: 		index	= noCaseFind(cmpkey,0);
   689: 
   690: 		while(index < buffer.size() && index != -1) {
   691: 
   692: 			if(isKey(index-1, (index) + types[i].size())) {
   693: 				colourKeys(index, types[i], "keytype");
   694: 			}
   695: 			index = noCaseFind(cmpkey,(index+cmpkey.size()+offset));
   696: 		}
   697: 	}
   698: }
   699: // checks for case sensitive keys ---------------------------------------------
   700: int Engine::noCaseFind(string search, int index) {
   701: 
   702: 	if(doCaseKeys) {
   703: 		return buffer.find(search,index);
   704: 	}
   705: 	if(search == "class") {
   706: 		return buffer.find(search,index);
   707: 	}
   708: 
   709: 	string tmp;
   710: 	tmp = buffer;
   711: 
   712: 	for(int i=0; i < tmp.size(); i++) {
   713: 		tmp[i] = toupper(tmp[i]);
   714: 	}
   715: 	for(int j=0; j < search.size(); j++) {
   716: 		search[j] = toupper(search[j]);
   717: 	}
   718: 
   719: 	return tmp.find(search,index);
   720: }
   721: // asserts word boundaries for keywords ---------------------------------------
   722: bool Engine::isKey(int before, int after) const {
   723: 
   724: 	if(buffer[before] == '#')   {return false;}
   725: 	if(buffer[before] == '_')   {return false;}
   726: 	if(buffer[after]  == '_')   {return false;}
   727: 	if(isalnum(buffer[before])) {return false;}
   728: 	if(isalnum(buffer[after]))  {return false;}
   729: 
   730: 	if(ispunct(buffer[before]) || isspace(buffer[before])) {
   731: 		if(ispunct(buffer[after]) || isspace(buffer[after])) {
   732: 			return true;
   733: 		}
   734: 	}
   735: 	return true;
   736: }
   737: // colourize the keywords -----------------------------------------------------
   738: void Engine::colourKeys(int index, string key, string cssclass) {
   739: 
   740: 	if(abortColour(index)) {
   741: 		return;
   742: 	}
   743: 	buffer.insert(index, "<font CLASS=" + cssclass + ">");
   744: 	buffer.insert(index+key.size()+20, "</font>");
   745: }
   746: //-----------------------------------------------------------------------------
   747: // parse for variables --------------------------------------------------------
   748: void Engine::parseVariable(string var) {
   749: 
   750: 	int index;
   751: 	int test;
   752: 
   753: 	index = buffer.find(var,0);
   754: 	test  = buffer.find("#",0);
   755: 	if(test != -1 && test < index) {return;}
   756: 
   757: 	while(index < string::npos) {
   758: 
   759: 		if(index != -1) {colourVariable(index);}
   760: 		index = buffer.find(var,index +22);
   761: 	}
   762: }
   763: // colourize the variables ----------------------------------------------------
   764: void Engine::colourVariable(int index) {
   765: 
   766: 	int end = 0;
   767: 	buffer.insert(index, "<font CLASS=preproc>");
   768: 
   769: 	int i = index+21;
   770: 
   771: 	// search for a variable name delimiter
   772: 	while(!end && i < buffer.size()) {
   773: 
   774: 		if(!isalnum(buffer[i])) {
   775: 			if(buffer[i] == '_')   {i++;}
   776: 			if(isspace(buffer[i])) {end = i;}
   777: 			if(ispunct(buffer[i])) {end = i;}
   778: 			if(buffer[i] ==  '=')  {end = i;}
   779: 			if(buffer[i] ==  ',')  {end = i;}
   780: 			if(buffer[i] ==  '{')  {end = i;}
   781: 			if(buffer[i] ==  '[')  {end = i;}
   782: 			if(buffer[i] ==  '(')  {end = i;}
   783: 			if(buffer[i] == '\'')  {end = i;}
   784: 			if(buffer[i] == '\n')  {end = i;}
   785: 		}
   786: 		if(i == buffer.size() -1)  {end = i+1;}
   787: 		else i++;
   788: 	}
   789: 
   790: 	if(buffer[end -1] == '\"')     {end--;}
   791: 	if(buffer[end -1] == ')')      {end--;}
   792: 
   793: 	buffer.insert(end, "</font>");
   794: }
   795: //-----------------------------------------------------------------------------
   796: // check for comments ---------------------------------------------------------
   797: void Engine::parseComment(string cmnt) {
   798: 
   799:   if(inComment) {return;}
   800: 
   801: 	int index = buffer.find(cmnt,0);
   802: 	if(index == -1) {return;}
   803: 
   804: // do not misktake HTML attributes for UNIX comments
   805: 	if(cmnt == "#" && index != -1 && buffer[index -1] != '\\') {
   806: 		if(index != 0) {
   807: 			while(buffer[index -1] == '=' && index < string::npos) {
   808: 				index = buffer.find("#",index+1);
   809: 			}
   810: 		}
   811: 	}	
   812: //-----------------------------------------------//
   813: 
   814: 	if(buffer[index -1] == '$')  {return;}
   815: 	if(buffer[index -1] == '\\') {return;}
   816: 
   817: 	colourComment(index);
   818: }
   819: // colour an inline comment ---------------------------------------------------
   820: void Engine::colourComment(int index) {
   821: 
   822: 	if(abortColour(index)) {
   823: 		return;
   824: 	}
   825: 	if(doCinComnt) {
   826: 		if(buffer.rfind("http:",index) == index -5) {
   827: 			return;
   828: 		}
   829: 	}
   830: 
   831: 	// no highlighting inside of comments
   832: 	eraseTags(index,0);
   833: 
   834: 	// insert the font tags
   835: 	buffer.insert(index, "<font CLASS=comment>");
   836: 	buffer.insert(buffer.size(), "</font>");
   837: }
   838: //-----------------------------------------------------------------------------
   839: void Engine::parseCharZeroComment(char zchar) {
   840: 
   841: 	if(buffer[0] == zchar) {colourComment(0);}
   842: }
   843: // here is where the parsing rules apply --------------------------------------
   844: void Engine::doParsing() {
   845: 
   846: 	if(opt_anchor) {
   847: 		makeAnchor();
   848: 	}
   849: 	if(opt_number) {
   850: 		makeMargin();
   851: 	}
   852: 
   853: 	IO.rline(buffer);
   854: 
   855: 	// preformat HTML escapes
   856: 	PRE_PARSE_CODE;
   857: 
   858: 	if(doSymbols) parseSymbol();
   859: 
   860: 	if(DEBUG_DO_PARSING) cerr << endl << 0 << ": buffer is: " << buffer << endl;
   861: 
   862: 	if(doLabels)   PARSE_LABELS;
   863: 
   864: 	if(doStrings)
   865: 	{
   866: 		PARSE_DBL_QUO_STRING;
   867: 		PARSE_SIN_QUO_STRING;
   868: 		PARSE_BCK_QUO_STRING;
   869: 	}
   870: 
   871: 	if(DEBUG_DO_PARSING) cerr << 1 << ": buffer is: " << buffer << endl;
   872: 
   873: 	if(doPreProc)  PARSE_PREPROCESSOR;
   874: 
   875: 	if(doPasComnt) PARSE_PAS_MOD2_COMNT;
   876: 	if(doHtmComnt) PARSE_A_MARKUP_COMNT;
   877: 	if(doBigComnt) PARSE_CLASSICC_COMNT;
   878: 	if(doHskComnt) PARSE_HASKL_98_COMNT;
   879: 	if(doHtmlTags) PARSE_HTML_TAGS;
   880: 
   881: 	if(doKeywords) PARSE_KEYWORDS;
   882: 
   883: 	if(DEBUG_DO_PARSING) cerr << 2 << ": buffer is: " << buffer << endl;
   884: 
   885: 	if(doScalars)  PARSE_SCALAR_VARIABL;
   886: 	if(doArrays)   PARSE_ARRAYS_VARIABL;
   887: 	if(doHashes)   PARSE_HASHED_VARIABL;
   888: 
   889: 	if(DEBUG_DO_PARSING) cerr << 3 << ": buffer is: " << buffer << endl;
   890: 
   891: 	if(doNumbers)  PARSE_NUMBERS;
   892: 
   893: 	if(DEBUG_DO_PARSING) cerr << 4 << ": buffer is: " << buffer << endl;
   894: 
   895: 	if(doAdaComnt) {PARSE_A_ADA_95_COMNT;}
   896: 	if(doAspComnt) {PARSE_A_MS_ASP_COMNT;}
   897: 	if(doCinComnt) {PARSE_C_INLINE_COMNT;}
   898: 	if(doUnxComnt) {PARSE_UNIXHASH_COMNT;}
   899: 	if(doAsmComnt) {PARSE_ASSEMBLY_COMNT;}
   900: 	if(doBatComnt) {PARSE_DBLCOLON_COMNT;}
   901: 	if(doRemComnt) {PARSE_REMINDER_COMNT;}
   902: 	if(doFtnComnt) {PARSE_ZFORTRAN_COMNT;}
   903:  	if(doTclComnt) {PARSE_ZEROHASH_COMNT;}
   904: 
   905: 	if(DEBUG_DO_PARSING) cerr << 5 << ": buffer is: " << buffer << endl;
   906: 
   907: 	hyperTagMe();
   908: 	hyperNameMe();
   909: 	hyperLinkMe();
   910: 
   911: 	if(DEBUG_DO_PARSING) cerr << 6 << ": buffer is: " << buffer << endl;
   912: 
   913: 	IO << buffer << "\n";
   914: 
   915: 	endComment = inComment;
   916: 
   917: 	lncount++;
   918: 	buffer = "";
   919: }
   920: //-----------------------------------------------------------------------------
   921: // write the initial HTML tags ------------------------------------------------
   922: void Engine::begHtml(string name) {
   923: 
   924: 	string gen;
   925: 	string style;
   926: 	string openht;
   927: 
   928: 	gen = "<!--\n\
   929: This file was generated by Web C Plus Plus software v0.8.0\n\
   930: Webcpp Copyright (C)2001, (C)2002, (C)2003 Jeffrey Bakker under the GNU GPL\n\
   931: Get webcpp at http://webcpp.sf.net\n-->\n\n";
   932: 
   933: 	if(opt_extcss) {
   934: 
   935: 		Scs2.writeCSS(Scs2.getThemeName() + ".css");
   936: 
   937: 		style = "<link rel=\"stylesheet\" type=\"text/css\" href=\""
   938:               + Scs2.getThemeName() + ".css\"/>\n";
   939: 
   940: 	} else {
   941: 	
   942: 		style = "<style type=\"text/css\">\n\n"
   943: 			  + Scs2.getCSSdata() + "</style>\n";
   944: 	}
   945: 
   946: 	openht = "<html>\n<head>\n<title>" + name + "</title>\n"
   947:            + style + "</head>\n<body>\n<pre>\n\n";
   948: 
   949: 	if(IO.isOredir()) {
   950: 		IO << "Content-Type: text/html\n\n";  // cgi-ready
   951: 	}
   952: 	IO << gen << openht;
   953: }
   954: // write the closing HTML tags ------------------------------------------------
   955: void Engine::endHtml() {
   956: 
   957: 	IO << "\n\n</pre>\n\n";
   958: 	if(opt_webcpp) {
   959: 
   960: 		string made;
   961: 		made = "<hr size=4>\n\
   962: <table cellpadding=3 cellspacing=3 bgcolor=#000000><tr>\n\
   963: <td bgcolor=#ff0000><tt><font size=+2 color=#000000>w</font></tt></td>\n\
   964: <td bgcolor=#ffbb00><tt><font size=+2 color=#000000>e</font></tt></td>\n\
   965: <td bgcolor=#ffff00><tt><font size=+2 color=#000000>b</font></tt></td>\n\
   966: <td bgcolor=#00ff00><tt><font size=+2 color=#000000>c</font></tt></td>\n\
   967: <td bgcolor=#0000ff><tt><font size=+2 color=#000000>p</font></tt></td>\n\
   968: <td bgcolor=#bb00ff><tt><font size=+2 color=#000000>p</font></tt></td>\n\
   969: </tr><tr><td colspan=6>\n\
   970: <a href=\"http://webcpp.sf.net\"><center><b>\
   971: <font color=#ffffff>web c plus plus</font></b></center>\n\
   972: </a></td></tr>\n\
   973: </table>";
   974: 
   975: 		IO << made;
   976: 	}
   977: 
   978: 	IO << "\n\n</body>\n</html>\n";
   979: }
   980: // place HTML tags without being stripped -------------------------------------
   981: void Engine::hyperTagMe() {
   982: 
   983: 	int index;
   984: 	index = buffer.find("TagMe:",0);
   985: 	if(index == -1) {return;}
   986: 	if(abortColour(index)) {
   987: 		return;
   988: 	}
   989: 	buffer.erase(index,6);
   990: 
   991: 	for(int i=index; i < buffer.size(); i++) {
   992: 		if	(buffer.substr(i,4) == "&lt;") buffer.replace(i,4, "<");
   993: 		else if	(buffer.substr(i,4) == "&gt;") buffer.replace(i,4, ">");
   994: 	}
   995: }
   996: // hyperlink a line of code ---------------------------------------------------
   997: void Engine::hyperLinkMe() {
   998: 
   999: 	int index;
  1000: 	index = buffer.find("LinkMe:",0);
  1001: 	if(index == -1) {return;}
  1002: 	if(abortColour(index)) {
  1003: 		return;
  1004: 	}
  1005: 	string link;
  1006: 	link = buffer.substr(index+7);
  1007: 	buffer.erase(index, buffer.size() - index);
  1008: 
  1009: 	buffer.insert(0, "<a href=\"" + link + "\">");
  1010: 	buffer += "</a>";
  1011: }
  1012: // anchor a line of code ------------------------------------------------------
  1013: void Engine::hyperNameMe() {
  1014: 
  1015: 	int index;
  1016: 	index = buffer.find("NameMe:",0);
  1017: 	if(index == -1) {return;}
  1018: 	if(abortColour(index)) {
  1019: 		return;
  1020: 	}
  1021: 	string name;
  1022: 	name = buffer.substr(index+7);
  1023: 	buffer.erase(index, buffer.size() - index);
  1024: 
  1025: 	buffer.insert(0, "<a name=\"" + name + "\">");
  1026: 	buffer += "</a>";
  1027: }
  1028: // automatically hyperlink included C/C++ files -------------------------------
  1029: void Engine::hyperIncludeMe() {
  1030: 
  1031: 	int incl, insr;
  1032: 
  1033: 	incl = buffer.find("#include",0);
  1034: 	if(incl == -1) {return;}
  1035: 
  1036: 	insr = buffer.find("\"",incl+1);
  1037: 	if(insr == -1) {return;}
  1038: 
  1039: 	string cmd;
  1040: 	string link;
  1041: 	link = buffer.substr(insr);
  1042: 	link = link.substr(0,link.find("\"</font>"));
  1043: 
  1044: 	if(opt_follow) {
  1045: 		// follow and process the include file
  1046: 
  1047: 		cmd = "webcpp " + link.substr(1) + " -A:k -H";
  1048: 		// retain switches from the current file
  1049: 		if(opt_bigtab) {
  1050: 			cmd += " -t";
  1051: 			if(tabwidth != 8) {
  1052: 				cmd += "=";
  1053: 				cmd += tw;
  1054: 			}
  1055: 		}
  1056: 		if(opt_webcpp) cmd += " -m";
  1057: 		if(opt_number) cmd += " -l";
  1058: 		if(opt_anchor) cmd += " -a";
  1059: 		if(opt_extcss) cmd += " -X";
  1060: 
  1061: 		if(Scs2.getThemeName() != "typical") {
  1062: 			cmd += " -c=" + Scs2.getThemeName();
  1063: 		}
  1064: 		if(Scs2.getImageFile() != "") {
  1065: 			cmd += " -i=" + Scs2.getImageFile();
  1066: 		}
  1067: 		cerr << "\nHyperInclude found " + link.substr(1) + "\n";
  1068: 		cerr << cmd << "\n";
  1069: 		system(cmd.data());
  1070: 	}
  1071: 	// make the hyperlink
  1072: 	link = "<a href=" + link + ".html\">";
  1073: 	buffer.insert(insr, link);
  1074: 	buffer.insert(buffer.size(),"</a>");
  1075: 
  1076: /*
  1077: 	string path;
  1078: 	int dir_idx = IO.ifile.rfind("/");
  1079: 	if(dir_idx != -1) {
  1080: 
  1081: 		path = IO.ifile.substr(0,dir_idx +1);
  1082: 		link = path + link;
  1083: 	}
  1084: */
  1085: }
  1086: //-----------------------------------------------------------------------------
  1087: 



w e b c p p
web c plus plus