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