"Fossies" - the Fresh Open Source Software Archive 
Member "ansifilter-2.18/src/codegenerator.cpp" (30 Jan 2021, 38264 Bytes) of package /linux/privat/ansifilter-2.18.tar.bz2:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "codegenerator.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.17_vs_2.18.
1 /***************************************************************************
2 codegenerator.cpp - description
3 -------------------
4 copyright : (C) 2007-2021 by Andre Simon
5 email : a.simon@mailbox.org
6 ***************************************************************************/
7
8
9 /*
10 This file is part of ANSIFilter.
11
12 ANSIFilter is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 ANSIFilter is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with ANSIFilter. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "codegenerator.h"
27
28 #ifdef WIN32
29 #include <windows.h>
30 #else
31 #include <unistd.h>
32 #endif
33
34 #include <cstdlib>
35 #include <cstring>
36 #include <fstream>
37 #include "version.h"
38
39 #include "pangogenerator.h"
40 #include "htmlgenerator.h"
41 #include "rtfgenerator.h"
42 #include "plaintextgenerator.h"
43 #include "texgenerator.h"
44 #include "latexgenerator.h"
45 #include "bbcodegenerator.h"
46 #include "svggenerator.h"
47
48 namespace ansifilter
49 {
50
51 CodeGenerator * CodeGenerator::getInstance(OutputType type)
52 {
53 CodeGenerator* generator=NULL;
54 switch (type) {
55 case TEXT:
56 generator = new PlaintextGenerator();
57 break;
58 case HTML:
59 generator = new HtmlGenerator();
60 break;
61 case PANGO:
62 generator = new PangoGenerator();
63 break;
64 case LATEX:
65 generator = new LaTeXGenerator();
66 break;
67 case TEX:
68 generator = new TeXGenerator();
69 break;
70 case RTF:
71 generator = new RtfGenerator();
72 break;
73 case BBCODE:
74 generator = new BBCodeGenerator();
75 break;
76 case SVG:
77 generator = new SVGGenerator();
78 break;
79
80 default:
81 break;
82 }
83 return generator;
84 }
85
86 CodeGenerator::CodeGenerator(ansifilter::OutputType type)
87 :in(NULL),
88 out(NULL),
89 tagIsOpen(false),
90 encoding("none"),
91 docTitle("Source file"),
92 fragmentOutput(false),
93 font("Courier New"),
94 fontSize("10pt"),
95
96 lineNumberWidth ( 5 ),
97 lineNumber ( 0 ),
98 showLineNumbers ( false ),
99
100 numberWrappedLines ( true ), //TODO add option
101 numberCurrentLine(false),
102 addAnchors(false),
103 applyDynStyles(false),
104 omitVersionInfo(false),
105 parseCP437(false),
106 parseAsciiBin(false),
107 parseAsciiTundra(false),
108
109 outputType(type),
110 ignoreFormatting(false),
111 readAfterEOF(false),
112 omitTrailingCR(false),
113 ignClearSeq(false),
114 ignCSISeq(false),
115 termBuffer(NULL),
116 curX(0),
117 curY(0),
118 memX(0),
119 memY(0),
120 maxY(0),
121 asciiArtWidth(80),
122 asciiArtHeight(150),
123 lineWrapLen(0)
124 {
125 elementStyle.setFgColour(rgb2html(workingPalette[0]));
126 }
127
128 CodeGenerator::~CodeGenerator()
129 {}
130
131
132 void CodeGenerator::setShowLineNumbers(bool flag)
133 {
134 showLineNumbers=flag;
135 }
136
137 void CodeGenerator::setFragmentCode(bool flag)
138 {
139 fragmentOutput=flag;
140 }
141
142 void CodeGenerator::setWrapNoNumbers(bool flag)
143 {
144 numberWrappedLines = flag;
145 }
146
147 void CodeGenerator::setParseCodePage437(bool flag){
148 parseCP437 = flag;
149 }
150
151 void CodeGenerator::setParseAsciiBin(bool flag){
152 parseAsciiBin = flag;
153 }
154
155 void CodeGenerator::setParseAsciiTundra(bool flag){
156 parseAsciiTundra = flag;
157 }
158
159 void CodeGenerator::setIgnoreClearSeq(bool flag) {
160 ignClearSeq = flag;
161 }
162
163 void CodeGenerator::setIgnoreCSISeq(bool flag) {
164 ignCSISeq = flag;
165 }
166
167 void CodeGenerator::setAsciiArtSize(int width, int height){
168 if (width>0) asciiArtWidth = width;
169 if (height>0) asciiArtHeight = height;
170 }
171
172 bool CodeGenerator::getFragmentCode()
173 {
174 return fragmentOutput;
175 }
176
177 void CodeGenerator::setFont(const string& s)
178 {
179 font = s;
180 }
181
182 void CodeGenerator::setFontSize(const string& s)
183 {
184 fontSize = s ;
185 }
186
187 void CodeGenerator::setTitle(const string & title)
188 {
189 if (!title.empty())
190 docTitle= title;
191 std::size_t found = docTitle.rfind("/");
192 if (found!=std::string::npos){
193 docTitle = docTitle.substr(found+1);
194 }
195 }
196
197 string CodeGenerator::getTitle()
198 {
199 return docTitle;
200 }
201
202 void CodeGenerator::setEncoding(const string& encodingName)
203 {
204 encoding = encodingName;
205 }
206
207 void CodeGenerator::setStyleSheet(const std::string& path)
208 {
209 styleSheetPath=path;
210 }
211
212 void CodeGenerator::setPreformatting ( WrapMode lineWrappingStyle,
213 unsigned int lineLength
214 )
215 {
216
217 lineWrapLen = lineLength;
218 }
219
220
221 void CodeGenerator::setApplyDynStyles(bool flag) {
222 applyDynStyles = flag;
223 }
224
225 void CodeGenerator::setSVGSize ( const string& w, const string& h )
226 {
227 width=w;
228 height=h;
229 }
230
231 ParseError CodeGenerator::generateFile (const string &inFileName,
232 const string &outFileName)
233 {
234
235 ParseError error=PARSE_OK;
236
237 in = (inFileName.empty()? &cin :new ifstream (inFileName.c_str() , std::ios::binary));
238
239
240 if (!in->fail() && error==PARSE_OK) {
241 out = (outFileName.empty()? &cout :new ofstream (outFileName.c_str()));
242 if ( out->fail()) {
243 error=BAD_OUTPUT;
244 }
245 }
246
247 if ( in->fail()) {
248 error=BAD_INPUT;
249 }
250 if (error==PARSE_OK) {
251 if (! fragmentOutput) {
252 *out << getHeader();
253 }
254
255 printBody();
256
257 if (! fragmentOutput) {
258 *out << getFooter();
259 }
260 }
261
262 if (!outFileName.empty()) {
263 delete out;
264 out=NULL;
265 }
266 if (!inFileName.empty()) {
267 delete in;
268 in=NULL;
269 }
270 return error;
271 }
272
273 string CodeGenerator::generateString(const string &input)
274 {
275 in = new istringstream (input);
276 out = new ostringstream ();
277
278 if ( in->fail() || out->fail()) {
279 return "";
280 }
281
282 if (! fragmentOutput) {
283 *out << getHeader();
284 }
285
286 printBody();
287
288 if (! fragmentOutput) {
289 *out << getFooter();
290 }
291
292 string result = static_cast<ostringstream*>(out)->str();
293
294 delete out;
295 out=NULL;
296 delete in;
297 in=NULL;
298
299 return result;
300 }
301
302 string CodeGenerator::generateStringFromFile(const string &inFileName)
303 {
304
305 in = new ifstream (inFileName.c_str() , std::ios::binary);
306 out = new ostringstream ();
307
308 if ( in->fail() || out->fail()) {
309 return "";
310 }
311
312 if (! fragmentOutput) {
313 *out << getHeader();
314 }
315
316 printBody();
317
318 if (! fragmentOutput) {
319 *out << getFooter();
320 }
321
322 string result = static_cast<ostringstream*>(out)->str();
323
324 delete out;
325 out=NULL;
326 delete in;
327 in=NULL;
328
329 return result;
330 }
331
332 ParseError CodeGenerator::generateFileFromString (const string &sourceStr,
333 const string &outFileName,
334 const string &title)
335 {
336 ParseError error=PARSE_OK;
337
338 in = new istringstream (sourceStr);
339 if (!in->fail()) {
340 out = (outFileName.empty()? &cout :new ofstream (outFileName.c_str()));
341 if ( out->fail()) {
342 error=BAD_OUTPUT;
343 }
344 }
345
346 if ( in->fail()) {
347 error=BAD_INPUT;
348 }
349
350 if (error==PARSE_OK) {
351
352 if (! fragmentOutput) {
353 *out << getHeader();
354 }
355
356 printBody();
357
358 if (! fragmentOutput) {
359 *out << getFooter();
360 }
361 }
362
363 if (!outFileName.empty()) {
364 delete out;
365 out=NULL;
366 }
367
368 delete in;
369 in=NULL;
370
371 return error;
372 }
373
374
375 bool CodeGenerator::printDynamicStyleFile ( const string &outPath )
376 {
377 return true;
378 }
379
380 /*
381
382 ESC[nL Inserts n blank lines at cursor line. (NANSI)
383 ESC[nM Deletes n lines including cursor line. (NANSI)
384 ESC[n@ Inserts n blank chars at cursor. (NANSI)
385 ESC[nP Deletes n chars including cursor char. (NANSI)
386 ESC[n;ny Output char translate (NANSI)
387 */
388
389 bool CodeGenerator::parseSGRParameters(const string& line, size_t begin, size_t end)
390 {
391 if (line.empty() || begin==end) { // fix empty grep --color ending sequence
392 elementStyle.setReset(true);
393 return true;
394 }
395
396 int ansiCode=0;
397 int colorCode=0;
398 unsigned char colorValues[3]= {0};
399
400 string codes=line.substr(begin, end-begin);
401 vector<string> codeVector = StringTools::splitString(codes, ';');
402
403 vector<string>::iterator itVectorData = codeVector.begin();
404 while( itVectorData != codeVector.end()) {
405 StringTools::str2num<int>(ansiCode, *(itVectorData), std::dec);
406 elementStyle.setReset(false);
407
408 switch (ansiCode) {
409 case 0:
410 elementStyle.setReset(true);
411 break;
412 case 1:
413 elementStyle.setBold(true);
414 elementStyle.setFgColour(rgb2html(workingPalette[8]));
415 break;
416 case 2: //Faint
417 break;
418
419 case 3:
420 elementStyle.setItalic(true);
421 break;
422
423 case 5: //Blink
424 case 6: //Blink fast
425 elementStyle.setBlink(true);
426 break;
427
428 case 7:
429 elementStyle.imageMode(true);
430 break;
431
432 case 8:
433 elementStyle.setConceal(true);
434 break;
435
436 case 4:// Underline Single
437 case 21: // Underline double
438 elementStyle.setUnderline(true);
439 break;
440
441 case 22:
442 elementStyle.setBold(false);
443 break;
444
445 case 24:
446 elementStyle.setUnderline(false);
447 break;
448
449 case 25:
450 elementStyle.setBlink(false);
451 break;
452
453 case 27:
454 elementStyle.imageMode(false);
455 break;
456
457 case 28:
458 elementStyle.setConceal(false);
459 break;
460
461 case 30:
462 case 31:
463 case 32:
464 case 33:
465 case 34:
466 case 35:
467 case 36:
468 case 37:
469 if (elementStyle.isBold()){
470 elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-30+8]));
471 } else
472 elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-30]));
473 break;
474
475 case 38: // xterm 256 foreground color mode \033[38;5;<color>
476
477 itVectorData++;
478 if (itVectorData == codeVector.end()) break;
479
480 if (*(itVectorData)=="5") {
481 itVectorData++;
482 if (itVectorData == codeVector.end()) break;
483
484 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
485 xterm2rgb((unsigned char)colorCode, colorValues);
486 elementStyle.setFgColour(rgb2html(colorValues));
487 } else if (*(itVectorData)=="2") {
488
489 itVectorData++;
490 if (itVectorData == codeVector.end()) break;
491 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
492 colorValues[0] = colorCode & 0xff;
493 itVectorData++;
494
495 if (itVectorData == codeVector.end()) break;
496 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
497 colorValues[1] = colorCode & 0xff;
498
499 itVectorData++;
500 if (itVectorData == codeVector.end()) break;
501 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
502 colorValues[2] = colorCode & 0xff;
503
504 elementStyle.setFgColour(rgb2html(colorValues));
505 }
506 break;
507
508 case 39:
509 elementStyle.setReset(true);
510 break;
511
512 case 40:
513 case 41:
514 case 42:
515 case 43:
516 case 44:
517 case 45:
518 case 46:
519 case 47:
520 elementStyle.setBgColour(rgb2html(workingPalette[ansiCode-40]));
521 break;
522
523 case 48: // xterm 256 background color mode \033[48;5;<color>
524
525 itVectorData++;
526 if (itVectorData == codeVector.end()) break;
527
528 if(*(itVectorData)=="5") {
529
530 itVectorData++;
531 if (itVectorData == codeVector.end()) break;
532
533 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
534 xterm2rgb((unsigned char)colorCode, colorValues);
535 elementStyle.setBgColour(rgb2html(colorValues));
536 } else if (*(itVectorData)=="2") {
537
538 itVectorData++;
539 if (itVectorData == codeVector.end()) break;
540 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
541 colorValues[0] = colorCode & 0xff;
542 itVectorData++;
543
544 if (itVectorData == codeVector.end()) break;
545 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
546 colorValues[1] = colorCode & 0xff;
547
548 itVectorData++;
549 if (itVectorData == codeVector.end()) break;
550 StringTools::str2num<int>(colorCode, *(itVectorData), std::dec);
551 colorValues[2] = colorCode & 0xff;
552
553 elementStyle.setBgColour(rgb2html(colorValues));
554 }
555
556 break;
557
558 case 49:
559 elementStyle.setReset(true);
560 break;
561
562 /*aixterm codes*/
563 case 90:
564 case 91:
565 case 92:
566 case 93:
567 case 94:
568 case 95:
569 case 96:
570 case 97:
571 elementStyle.setFgColour(rgb2html(workingPalette[ansiCode-90+8]));
572 break;
573
574 case 100:
575 case 101:
576 case 102:
577 case 103:
578 case 104:
579 case 105:
580 case 106:
581 case 107:
582 elementStyle.setBgColour(rgb2html(workingPalette[ansiCode-100+8]));
583 break;
584 }
585
586 // Set RTF color index
587 // 8 default colours followed by bright colors see rtfgenerator.cpp
588 if (ansiCode>=30 and ansiCode <=37)
589 elementStyle.setFgColourID(ansiCode-30 + (elementStyle.isBold()? 8 : 0) );
590 else if (ansiCode>=90 and ansiCode <98)
591 elementStyle.setFgColourID(ansiCode-90+8);
592
593 else if (ansiCode>=40 and ansiCode <=47)
594 elementStyle.setBgColourID(ansiCode-40 /*+ elementStyle.isBold()? 8 : 0 */);
595 else if (ansiCode>=100 and ansiCode <108)
596 elementStyle.setBgColourID(ansiCode-100+8);
597
598 if (itVectorData != codeVector.end()) itVectorData++;
599 }
600
601 return true;
602 }
603
604
605 void CodeGenerator::parseCodePage437Seq(string line, size_t begin, size_t end){
606
607 string codes=line.substr(begin, end-begin);
608 vector<string> codeVector = StringTools::splitString(codes, ',');
609
610 if (line[end]=='H'){
611 codeVector = StringTools::splitString(codes, ';');
612
613 curX = curY = 0;
614 if (codeVector.size()==1) {
615 curY = atoi(codeVector[0].c_str());
616 } else if (codeVector.size()==2){
617 curY = atoi(codeVector[0].c_str());
618 curX = atoi(codeVector[1].c_str());
619 }
620
621 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
622 }
623
624 if (line[end]=='A'){
625 if (codeVector.size()==1){
626 curY -= atoi(codeVector[0].c_str());
627 } else {
628 curY--;
629 }
630 }
631
632 if (line[end]=='B'){
633 if (codeVector.size()==1){
634 curY += atoi(codeVector[0].c_str());
635 } else {
636 curY++;
637 }
638 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
639 }
640
641 if (line[end]=='C'){
642
643 if (codeVector.size()==1){
644 curX += atoi(codeVector[0].c_str());
645 } else {
646 curX++;
647 }
648
649 //handle coloumn overflow
650 if (curX>asciiArtWidth && curY<asciiArtHeight){
651 curX-=asciiArtWidth;
652 curY++;
653 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
654 }
655 }
656
657 if (line[end]=='D'){
658 if (codeVector.size()==1){
659 curX -= atoi(codeVector[0].c_str());
660 } else {
661 curX--;
662 }
663 if (curX<0) curX=0;
664 }
665
666 if (line[end]=='J'){
667 // std::cerr<<"J!!!\n";
668 /*
669 if (codeVector.size()==1 && codeVector[0]=="2"){
670 for (int i=0;i<100*100;i++) *termBuffer[i]->c =0;
671 }
672 */
673 }
674
675 if (line[end]=='K'){
676 // std::cerr<<"K!!!\n";
677 // for (int i=curX;i<asciiArtWidth;i++) termBuffer[curY*asciiArtWidth + i]->c =0;
678
679 }
680
681 if (line[end]=='s'){
682 memX = curX;
683 memY = curY;
684 memStyle = elementStyle;
685 }
686 if (line[end]=='u'){
687 curX = memX;
688 curY = memY;
689 elementStyle=memStyle;
690 }
691
692 }
693
694 void CodeGenerator::insertLineNumber ()
695 {
696 if ( showLineNumbers && !parseCP437) {
697
698 ostringstream lnum;
699 lnum << setw ( 5 ) << right;
700 if( numberCurrentLine ) {
701 *out << getCloseTag();
702 lnum << lineNumber;
703 *out <<lnum.str()<<spacer;
704 *out << getOpenTag();
705 } else {
706 *out << lnum.str(); //for indentation
707 }
708 }
709 }
710
711 void CodeGenerator::printTermBuffer() {
712
713 for (int y=0;y<=maxY;y++) {
714
715 for (int x=0;x<asciiArtWidth;x++) {
716 if (termBuffer[x + y* asciiArtWidth].c=='\r') {
717 break;
718 }
719 elementStyle = termBuffer[x + y* asciiArtWidth].style;
720
721 //full block
722 if (termBuffer[x + y* asciiArtWidth].c == 0xdb){
723 elementStyle.setBgColour(elementStyle.getFgColour());
724 }
725
726 if (!elementStyle.isReset()) {
727 *out <<getOpenTag();
728 }
729
730 *out << maskCP437Character(termBuffer[x + y* asciiArtWidth].c);
731
732 if (!elementStyle.isReset()) {
733 *out <<getCloseTag();
734 }
735 }
736 *out<<newLineTag;
737 }
738 out->flush();
739 delete [] termBuffer;
740 }
741
742
743 void CodeGenerator::parseBinFile(){
744 char buffer [2] = {0};
745 int cur=0;
746 int next=0;
747 int count=0;
748 allocateTermBuffer();
749 while (in->read (buffer, 2)){
750
751 cur = buffer[0]&0xff;
752 next = buffer[1]&0xff;
753
754 int colBg = (next & 240) >> 4;
755 int colFg = (next & 15);
756
757 if (colBg > 8)
758 {
759 colBg -= 8;
760 }
761
762 elementStyle.setFgColour(rgb2html(workingPalette[colFg]));
763 elementStyle.setBgColour(rgb2html(workingPalette[colBg]));
764
765 //FIXME:
766 elementStyle.setBold(cur >= 0x20 && cur <= 0x7a);
767
768 if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
769 termBuffer[curX + curY*asciiArtWidth].c = cur;
770 termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
771 curX++;
772 }
773 if (count % asciiArtWidth == 0 ) {
774 curY++;
775 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
776 curX=0;
777 }
778 count+=2;
779 }
780 }
781
782 // the XBIN decoding function is based on AnsiLove:
783 // https://github.com/ansilove/
784 void CodeGenerator::parseXBinFile(){
785
786 char header [11] = {0};
787 char palette [48] = {0};
788
789 if (in->read(header, 11)){
790
791 asciiArtWidth = 0xff & ((header[ 6 ] << 8) + header[ 5 ]);
792 asciiArtHeight = 0xff & ((header[ 8 ] << 8) + header[ 7 ]);
793 int fontSize = header[ 9 ];
794 int flags = header[ 10 ];
795 /*
796 std::cerr<<"XBIN width:"<<asciiArtWidth<<"\n";
797 std::cerr<<"XBIN height:"<<asciiArtHeight<<"\n";
798 std::cerr<<"XBIN fontsize:"<<fontSize<<"\n";
799 std::cerr<<"XBIN flags:"<<flags<<"\n";
800 */
801 allocateTermBuffer();
802
803 if( (flags & 1) == 1 && in->read(palette, 48)) {
804 int loop;
805 int index;
806
807 //override default palette
808 //TODO allow user to override this with a map?
809 for (loop = 0; loop < 16; loop++)
810 {
811 index = loop * 3;
812 workingPalette[loop][0] = palette[index] << 2 | palette[index] >> 4;
813 workingPalette[loop][1] = palette[index+1] << 2 | palette[index+1] >> 4;
814 workingPalette[loop][2] = palette[index+2] << 2 | palette[index+2] >> 4;
815 }
816 }
817
818 // skip font
819 if( (flags & 2) == 2 ) {
820 int numchars = ( flags & 0x10 ? 512 : 256 );
821 in->seekg ( fontSize * numchars, ios::cur);
822 }
823
824 // decode image
825 if( (flags & 4) == 4) {
826 int c=0;
827 while( in && curY < asciiArtHeight)
828 {
829 c = in->get();
830 int compression = c & 0xC0;
831 int cnt = ( c & 0x3F ) + 1;
832
833 int cur = -1;
834 int attr = -1;
835
836 while( cnt-- ) {
837 // none
838 if( compression == 0 ) {
839 cur = in->get();
840 attr = in->get();
841 }
842 // char
843 else if ( compression == 0x40 ) {
844 if( cur == -1 ) {
845 cur = in->get();
846 }
847 attr = in->get();
848 }
849 // attr
850 else if ( compression == 0x80 ) {
851 if( attr == -1 ) {
852 attr = in->get();
853 }
854 cur = in->get();
855 }
856 // both
857 else {
858 if( cur == -1 ) {
859 cur = in->get();
860 }
861 if( attr == -1 ) {
862 attr = in->get();
863 }
864 }
865
866 int colBg = (attr & 240) >> 4;
867 int colFg = (attr & 15);
868
869 if (colBg > 8)
870 {
871 colBg -= 8;
872 }
873
874 elementStyle.setFgColour(rgb2html(workingPalette[colFg]));
875 elementStyle.setBgColour(rgb2html(workingPalette[colBg]));
876
877 //FIXME:
878 elementStyle.setBold(cur >= 0x20 && cur <= 0x7a);
879
880 if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
881 termBuffer[curX + curY*asciiArtWidth].c = cur;
882 termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
883 curX++;
884 }
885
886 if (curX == asciiArtWidth)
887 {
888 curX = 0;
889 curY++;
890 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
891 }
892 }
893 }
894 } else {
895 //flat BIN
896 parseBinFile();
897 }
898 }
899 }
900
901 // the TND decoding function is based on AnsiLove:
902 // https://github.com/ansilove/
903 void CodeGenerator::parseTundraFile(){
904 char buffer [10] = {0};
905 bool isTundra = false;
906 if ( in->read (buffer, 9) ) {
907 isTundra = string(buffer)=="\x18TUNDRA24";
908 }
909
910 if (!isTundra) {
911 std::cerr<<"not a Tundra file\n";
912 return;
913 }
914
915 asciiArtWidth = 80;
916
917 allocateTermBuffer();
918
919 int fg_red=0, fg_green=0, fg_blue=0;
920 int bg_red=0, bg_green=0, bg_blue=0;
921 while (in->read (buffer, 1)){
922
923 if (curX >= asciiArtWidth){
924 curX = 0;
925 curY ++;
926 }
927
928 int cur = buffer[0]&0xff;
929
930 if (cur==1) {
931 if (!in->read (buffer, 8)) goto GENTLE_EXIT;
932
933 curY = ((buffer[0] << 24)& 0xff) + ((buffer[1] << 16)& 0xff) + ((buffer[2] << 8)& 0xff) + (buffer[3]& 0xff);
934 curX = ((buffer[4] << 24)& 0xff) + ((buffer[5] << 16)& 0xff) + ((buffer[6] << 8)& 0xff) + (buffer[7]& 0xff);
935 }
936
937 if (cur == 2)
938 {
939 if (!in->read (buffer, 5) ) goto GENTLE_EXIT;
940
941 fg_red=buffer[2];
942 fg_green=buffer[3];
943 fg_blue=buffer[4];
944 cur= buffer[0];
945 }
946
947 if (cur == 4)
948 {
949 if (!in->read (buffer, 5) ) goto GENTLE_EXIT;
950 bg_red=buffer[2];
951 bg_green=buffer[3];
952 bg_blue=buffer[4];
953 cur= buffer[0];
954 }
955
956 if (cur==6)
957 {
958 if (!in->read (buffer, 10) ) goto GENTLE_EXIT;
959
960 fg_red=buffer[2];
961 fg_green=buffer[3];
962 fg_blue=buffer[4];
963
964 bg_red=buffer[6];
965 bg_green=buffer[7];
966 bg_blue=buffer[8];
967
968 cur = buffer[0];
969 }
970
971 if (cur !=1 && cur !=2 && cur !=4 && cur !=6)
972 {
973 elementStyle.setFgColour(rgb2html( fg_red&0xff, fg_green&0xff, fg_blue&0xff));
974 elementStyle.setBgColour(rgb2html( bg_red&0xff, bg_green&0xff, bg_blue&0xff ));
975
976 termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
977
978 termBuffer[curX + curY*asciiArtWidth].c = cur &0xff;
979 curX++;
980 }
981 }
982
983 GENTLE_EXIT:
984
985 maxY = curY<asciiArtHeight ? curY : asciiArtHeight;
986 }
987
988 void CodeGenerator::allocateTermBuffer(){
989
990 if (termBuffer) delete [] termBuffer;
991
992 termBuffer = new TDChar[asciiArtWidth*asciiArtHeight];
993 for (int i=0; i<asciiArtWidth*asciiArtHeight; i++){
994 termBuffer[i].c=0;
995 }
996 }
997
998 bool CodeGenerator::streamIsXBIN() {
999 if (in==&cin) return false;
1000
1001 bool isXBIN = false;
1002 char head[5] = {0};
1003 if (in->read (head, sizeof head -1) ) {
1004 isXBIN = string(head)=="XBIN";
1005 }
1006 in->clear();
1007 in->seekg (0, ios::beg);
1008 return isXBIN;
1009 }
1010
1011
1012 bool CodeGenerator::streamIsTundra() {
1013 if (in==&cin) return false;
1014
1015 bool isTND = false;
1016 char head[10] = {0};
1017
1018 if (in->read (head, sizeof head -1) ) {
1019 isTND = string(head)=="\x18TUNDRA24";
1020 }
1021 in->clear();
1022 in->seekg (0, ios::beg);
1023 return isTND;
1024 }
1025 ////////////////////////////////////////////////////////////////////////////
1026
1027 void CodeGenerator::processInput()
1028 {
1029 int cur=0;
1030 int next=0;
1031
1032 if (parseCP437 || parseAsciiBin || parseAsciiTundra){
1033 elementStyle.setReset(false);
1034 }
1035
1036 // deal with BIN/XBIN without file watching, reformatting and line numbering distractions
1037 if (parseAsciiBin){
1038
1039 if (streamIsXBIN())
1040 parseXBinFile();
1041 else
1042 parseBinFile();
1043
1044 printTermBuffer();
1045 return;
1046 }
1047
1048 if (parseAsciiTundra && streamIsTundra()){
1049 parseTundraFile();
1050
1051 printTermBuffer();
1052 return;
1053 }
1054
1055
1056 // handle normal text files
1057 if (readAfterEOF && in!=&cin) {
1058 in->seekg (0, ios::end);
1059 // output the last few lines or the complete file if not too big
1060 if (in->tellg()>51200) {
1061 in->seekg (-512, ios::end);
1062 // output complete lines, ignore cur line fragment
1063 in->ignore(512, '\n');
1064 } else {
1065 in->seekg (0, ios::beg); // output complete file
1066 }
1067 }
1068
1069 if (streamIsXBIN()) {
1070 *out<<"Please apply --art-bin option for XBIN files.\n";
1071 return;
1072 }
1073
1074 if (streamIsTundra()) {
1075 *out<<"Please apply --art-tundra option for TND files.\n";
1076 return;
1077 }
1078
1079 string line;
1080 size_t i=0;
1081 size_t plainTxtCnt=0;
1082 bool tagOpen=false;
1083 bool isGrepOutput=false;
1084 bool isKSeq=false;
1085
1086 bool omitNewLine=false;
1087 lineNumber=0;
1088
1089 if (parseCP437){
1090 allocateTermBuffer();
1091 }
1092
1093 while (true) {
1094
1095 bool eof=false;
1096
1097 eof=!getline(*in, line);
1098
1099 if( !omitNewLine )
1100 ++lineNumber;
1101
1102 numberCurrentLine = true;
1103
1104 if (eof) {
1105 // imitate tail bahaviour, continue to read after EOF
1106 if (readAfterEOF) {
1107 out->flush();
1108 in->clear();
1109 #ifdef WIN32
1110 Sleep(250);
1111 #else
1112 sleep(1);
1113 #endif
1114 } else {
1115 if (!parseCP437 && !omitTrailingCR)
1116 printNewLine(outputType!=TEXT);
1117 break;
1118 }
1119 } else {
1120
1121 if (!omitNewLine && !parseCP437 && lineNumber>1)
1122 printNewLine();
1123
1124 if (!omitNewLine )
1125 insertLineNumber();
1126
1127 omitNewLine = false;
1128
1129 i=0;
1130 plainTxtCnt=0;
1131 size_t seqEnd=string::npos;
1132
1133 while (i <line.length() ) {
1134 // CSI ?
1135 cur = line[i]&0xff;
1136
1137 if (parseCP437){
1138
1139 if (cur==0x1b && line.length() - i > 2){
1140 next = line[i+1]&0xff;
1141 if (next==0x5b) {
1142 i+=2;
1143 seqEnd = i;
1144 //find sequence end
1145 while ( seqEnd<line.length()
1146 && (line[seqEnd]<0x40 || line[seqEnd]>0x7e )) {
1147 ++seqEnd;
1148 }
1149
1150 if ( line[seqEnd]=='m' ) {
1151 parseSGRParameters(line, i, seqEnd);
1152 } else {
1153 parseCodePage437Seq(line, i, seqEnd);
1154 }
1155 i=seqEnd+1;
1156 }
1157 else {
1158 ++i;
1159 }
1160 } else if (cur==0x1a && line.length() - i > 6){
1161 // skip SAUCE info section
1162 while (line[i]==0x1a || !line[i]) ++i;
1163 if (line.substr(i, 5)=="SAUCE"){
1164 break;
1165 }
1166 } else {
1167 if (curX>=0 && curX<asciiArtWidth && curY>=0 && curY<asciiArtHeight){
1168 termBuffer[curX + curY*asciiArtWidth].c = line[i];
1169 termBuffer[curX + curY*asciiArtWidth].style = elementStyle;
1170 curX++;
1171 }
1172
1173 if (curX==asciiArtWidth || line[i]=='\r' ) {
1174 curY++;
1175 if (maxY<curY && curY<asciiArtHeight) maxY=curY;
1176 curX=0;
1177 if (line[i]=='\r') i=line.length();
1178 }
1179 ++i;
1180 }
1181 } else {
1182
1183 if ( (cur&0xff)==0x0d && i<line.length()-1) {
1184 plainTxtCnt-=i;
1185
1186 lineBuf.seekp(showLineNumbers ? 0 : 0, ios::beg);
1187 lineBuf<<getOpenTag();
1188 }
1189 // wrap line
1190 if (lineWrapLen && plainTxtCnt && plainTxtCnt % lineWrapLen==0) {
1191 ++lineNumber;
1192 printNewLine();
1193 insertLineNumber();
1194 plainTxtCnt=0;
1195 }
1196
1197 if ( line.length() - i > 2 && (line[i+1]&0xff)==0x08) i++;
1198 if ( cur==0x07) {
1199 ++lineNumber;
1200 printNewLine();
1201 insertLineNumber();
1202 }
1203
1204 if ( cur==0x1b || (!ignCSISeq && ( cur==0x9b || cur==0xc2)) ) {
1205
1206 if (line.length() - i > 2){
1207 next = line[i+1]&0xff;
1208
1209 //move index behind CSI
1210 if ( (cur==0x1b && next==0x5b) || ( cur==0xc2 && next==0x9b) ) {
1211 ++i;
1212 } else {
1213 // restore a unicode sequence if the two digit CSI is not matched
1214 // ansiweather -l Berlin,DE | ansifilter -T
1215 if (cur==0xc2 || cur==0x1b) {
1216 lineBuf << maskCharacter(cur);
1217 ++plainTxtCnt;
1218 }
1219
1220 }
1221
1222 // http://linuxcommand.org/lc3_adv_tput.php
1223 // http://ascii-table.com/ansi-escape-sequences-vt-100.php
1224 if (next==0x28){ // ( -> maybe need to handle more codes here
1225 if (line[i+2]==0x42) { // B
1226 elementStyle.setReset(false);
1227 i+=2;
1228 }
1229 }
1230
1231 // https://iterm2.com/documentation-escape-codes.html
1232 if (next==0x5d) {
1233
1234 if (line[i+2]=='8') {
1235
1236 size_t uriBegin = line.find(';', i+4);
1237 seqEnd = line.find("\x1b]8;;\x07", i);
1238 size_t uriDelim = line.find(0x07, uriBegin+1);
1239
1240 if (uriBegin != string::npos && seqEnd != string::npos){
1241 string uri = line.substr(uriBegin+1, uriDelim - uriBegin - 1 );
1242 string txt = line.substr(uriDelim+1, seqEnd - uriDelim - 1);
1243 lineBuf << getHyperlink(uri, txt);
1244 i=seqEnd+4;
1245 }
1246 }
1247 ++i;
1248 }
1249
1250 if (i<line.size()) ++i;
1251
1252 if (line[i-1]==0x5b || (line[i-1]&0xff)==0x9b){
1253 seqEnd=i;
1254 //find sequence end
1255 while ( seqEnd<line.length()
1256 && (line[seqEnd]<0x40 || line[seqEnd]>0x7e )) {
1257 ++seqEnd;
1258 }
1259
1260 if ( line[seqEnd]=='m' && !ignoreFormatting ) {
1261 if (!elementStyle.isReset()) {
1262 lineBuf << getCloseTag();
1263 tagOpen=false;
1264 }
1265 parseSGRParameters(line, i, seqEnd);
1266 if (!elementStyle.isReset()) {
1267 lineBuf << getOpenTag();
1268 tagOpen=true;
1269 }
1270 }
1271
1272 // fix K sequences (iterm2/grep)
1273 isKSeq = line[seqEnd]=='K' && !ignClearSeq ;
1274 isGrepOutput = isKSeq && isascii(line[seqEnd+1]) && line[seqEnd+1] !=13 && line[seqEnd+1] != 27;
1275
1276 if ( line[seqEnd]=='s' || line[seqEnd]=='u'
1277 || (isKSeq && !isGrepOutput) ){
1278 i=line.length()+1;
1279 omitNewLine = isKSeq; // \n may follow K
1280 //FIXME std::cerr << "K CASE!\n";
1281 }
1282 else {
1283 i = 1 + ((seqEnd!=line.length())?seqEnd:i);
1284 }
1285 } else {
1286 cur= line[i-1]&0xff;
1287 next = line[i]&0xff;
1288
1289 //ignore content of two and single byte sequences (no CSI)
1290 if (cur==0x1b && ( next==0x50 || next==0x5d || next==0x58
1291 || next==0x5e||next==0x5f ) ) // DECSC seq
1292 {
1293 seqEnd=i;
1294 //find string end
1295 while ( seqEnd<line.length()
1296 && (line[seqEnd]&0xff)!=0x9e
1297 && line[seqEnd]!=0x07
1298 && (line[seqEnd]&0xff)!=0x3b ) {
1299 ++seqEnd;
1300 }
1301
1302 if (line[seqEnd+1]=='A')
1303 seqEnd++;
1304
1305 i=seqEnd+1;
1306 } else if (cur==0x1b && (
1307 next==0x37 || next==0x38
1308
1309 ) ) // DECSC seq
1310 {
1311 if (line[seqEnd+1]==0x1b)
1312 ++i;
1313 }
1314 }
1315 } else {
1316 ++i;
1317 }
1318 } else if (!ignCSISeq && (cur==0x90 || cur==0x9d || cur==0x98 || cur==0x9e ||cur==0x9f)) {
1319 seqEnd=i;
1320 //find string end
1321 while ( seqEnd<line.length() && (line[seqEnd]&0xff)!=0x9e
1322 && line[seqEnd]!=0x07 ) {
1323 ++seqEnd;
1324 }
1325 // handle false positives in unicode sequences
1326 // TODO fix set terminal title CSI (testansi.py)
1327 if (seqEnd<line.length() ) {
1328 i=seqEnd+1;
1329 } else {
1330 lineBuf << maskCharacter(line[i]);
1331 ++i;
1332 ++plainTxtCnt;
1333 }
1334 } else {
1335 // output printable character
1336 lineBuf << maskCharacter(line[i]);
1337 ++i;
1338 ++plainTxtCnt;
1339 }
1340 }
1341 }
1342 }
1343 } // while (true)
1344
1345 if (tagOpen) {
1346 *out <<getCloseTag();
1347 }
1348
1349 if (parseCP437){
1350 printTermBuffer();
1351 }
1352 out->flush();
1353 }
1354
1355
1356 void CodeGenerator::printNewLine(bool eof) {
1357
1358 string lineStr(lineBuf.str());
1359 if (eof) {
1360 lineStr = lineStr.substr(0, lineBuf.tellp());
1361 }
1362 *out << lineStr;
1363 *out << newLineTag;
1364
1365 lineBuf.clear();
1366 lineBuf.str(std::string());
1367 }
1368
1369
1370 /* the following functions are based on Wolfgang Frischs xterm256 converter utility:
1371 http://frexx.de/xterm-256-notes/
1372 */
1373 void CodeGenerator::xterm2rgb(unsigned char color, unsigned char* rgb)
1374 {
1375 // 16 basic colors
1376 if(color<16) {
1377 rgb[0] = workingPalette[color][0];
1378 rgb[1] = workingPalette[color][1];
1379 rgb[2] = workingPalette[color][2];
1380 }
1381
1382 // color cube color
1383 if(color>=16 && color<=232) {
1384 color-=16;
1385 rgb[0] = valuerange[(color/36)%6];
1386 rgb[1] = valuerange[(color/6)%6];
1387 rgb[2] = valuerange[color%6];
1388 }
1389
1390 // gray tone
1391 if(color>232) {
1392 rgb[0]=rgb[1]=rgb[2] = 8+(color-232)*0x0a;
1393 }
1394 }
1395
1396 string CodeGenerator::rgb2html(unsigned char* rgb){
1397 char colorString[10]= {0};
1398 sprintf(colorString, "#%02x%02x%02x", rgb[0], rgb[1], rgb[2]);
1399 return string(colorString);
1400 }
1401
1402 string CodeGenerator::rgb2html(int r, int g, int b){
1403 char colorString[10]= {0};
1404 sprintf(colorString, "#%02x%02x%02x", r, g, b);
1405 return string(colorString);
1406 }
1407
1408 const unsigned char CodeGenerator::valuerange[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };
1409
1410 unsigned char CodeGenerator::defaultPalette[16][3] = {
1411 { 0x00, 0x00, 0x00 }, // 0 ColorBlack
1412 { 0xCD, 0x00, 0x00 }, // 1 ColorRed
1413 { 0x00, 0xCD, 0x00 }, // 2 ColorGreen
1414 { 0xCD, 0xCD, 0x00 }, // 3 ColorYellow
1415 { 0x00, 0x00, 0xEE }, // 4 ColorBlue
1416 { 0xCD, 0x00, 0xCD }, // 5 ColorMagenta
1417 { 0x00, 0xCD, 0xCD }, // 6 ColorCyan
1418 { 0xE5, 0xE5, 0xE5 }, // 7 ColorGray
1419 { 0x7F, 0x7F, 0x7F }, // 8 ColorDarkGray
1420 { 0xFF, 0x00, 0x00 }, // 9 ColorBrightRed
1421 { 0x00, 0xFF, 0x00 }, // 10 ColorBrightGreen
1422 { 0xFF, 0xFF, 0x00 }, // 11 ColorBrightYellow
1423 { 0x5C, 0x5C, 0xFF }, // 12 ColorBrightBlue
1424 { 0xFF, 0x00, 0xFF }, // 13 ColorBrightMagenta
1425 { 0x00, 0xFF, 0xFF }, // 14 ColorBrightCyan
1426 { 0xFF, 0xFF, 0xFF } // 15 ColorBrightWhite
1427 };
1428
1429 unsigned char CodeGenerator::workingPalette[16][3] = {
1430 { 0x00, 0x00, 0x00 }, // 0 ColorBlack
1431 { 0xCD, 0x00, 0x00 }, // 1 ColorRed
1432 { 0x00, 0xCD, 0x00 }, // 2 ColorGreen
1433 { 0xCD, 0xCD, 0x00 }, // 3 ColorYellow
1434 { 0x00, 0x00, 0xEE }, // 4 ColorBlue
1435 { 0xCD, 0x00, 0xCD }, // 5 ColorMagenta
1436 { 0x00, 0xCD, 0xCD }, // 6 ColorCyan
1437 { 0xE5, 0xE5, 0xE5 }, // 7 ColorGray
1438 { 0x7F, 0x7F, 0x7F }, // 8 ColorDarkGray
1439 { 0xFF, 0x00, 0x00 }, // 9 ColorBrightRed
1440 { 0x00, 0xFF, 0x00 }, // 10 ColorBrightGreen
1441 { 0xFF, 0xFF, 0x00 }, // 11 ColorBrightYellow
1442 { 0x5C, 0x5C, 0xFF }, // 12 ColorBrightBlue
1443 { 0xFF, 0x00, 0xFF }, // 13 ColorBrightMagenta
1444 { 0x00, 0xFF, 0xFF }, // 14 ColorBrightCyan
1445 { 0xFF, 0xFF, 0xFF } // 15 ColorBrightWhite
1446 };
1447
1448 bool CodeGenerator::setColorMap(const string& mapPath){
1449
1450 //restore default colors
1451 if (mapPath.length()==0){
1452 memcpy(workingPalette, defaultPalette, sizeof defaultPalette);
1453 return true;
1454 }
1455
1456 ifstream mapFile ( mapPath.c_str() );
1457 if ( mapFile ) {
1458
1459 string line;
1460
1461 unsigned int idx=0;
1462 char sep='\0';
1463
1464 string colorCode;
1465 while ( getline ( mapFile, line ) ) {
1466 stringstream s(line);
1467
1468 s>>idx;
1469 if (idx>15) return false;
1470
1471 s>>sep;
1472 if (sep!='=') return false;
1473
1474 s>>colorCode;
1475 if (colorCode.size()>=7 && colorCode[0]=='#' ) {
1476 workingPalette[idx][0] = (char)std::strtol(colorCode.substr ( 1, 2 ).c_str(), NULL, 16);
1477 workingPalette[idx][1] = (char)std::strtol(colorCode.substr ( 3, 2 ).c_str(), NULL, 16);
1478 workingPalette[idx][2] = (char)std::strtol(colorCode.substr ( 5, 2 ).c_str(), NULL, 16);
1479 } else {
1480 return false;
1481 }
1482 }
1483 mapFile.close();
1484 } else {
1485 return false;
1486 }
1487 return true;
1488 }
1489
1490 }