1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 namespace System
  7 {
  8     public class delegate void ColorCharOutputMethod(Color.Constant textColorColor.Constant backColorint handleuchar c);
  9 
 10     public void AnsiProcess(int handleconst string& textconst ColorCharOutputMethod& writeOutputChar)
 11     {
 12         if (handle == 1)
 13         {
 14             AnsiEngine.Out().Process(textwriteOutputChar);
 15         }
 16         else if (handle == 2)
 17         {
 18             AnsiEngine.Error().Process(textwriteOutputChar);
 19         }
 20         else
 21         {
 22             throw Exception("AnsiEngine: invalid handle value " + ToString(handle) + " (not 1 or 2)");
 23         }
 24     }
 25 
 26     public class AnsiEngine
 27     {
 28         static AnsiEngine() : 
 29             out(new AnsiEngine(1))
 30             error(new AnsiEngine(2))
 31         {
 32         }
 33         public static AnsiEngine& Out()
 34         {
 35             return *out;
 36         }
 37         public static AnsiEngine& Error()
 38         {
 39             return *error;
 40         }
 41         public void Process(const string& textconst ColorCharOutputMethod& writeOutputChar)
 42         {
 43             for (char c : text)
 44             {
 45                 Put(cwriteOutputChar);
 46             }
 47         }
 48         public inline nothrow Color.Constant DefaultTextColor() const
 49         {
 50             return defaultTextColor;
 51         }
 52         public inline nothrow Color.Constant DefautBackColor() const
 53         {
 54             return defaultBackColor;
 55         }
 56         public nothrow void SetDefaultTextColor(Color.Constant color)
 57         {
 58             defaultTextColor = color;
 59         }
 60         public nothrow void SetDefaultBackColor(Color.Constant color)
 61         {
 62             defaultBackColor = color;
 63         }
 64         private nothrow AnsiEngine(int handle_) : handle(handle_)state(0)unicodeEngine()
 65             defaultTextColor(Color.Constant.gray)defaultBackColor(Color.Constant.black)
 66             currentTextColor(defaultTextColor)currentBackColor(defaultBackColor)
 67             textColor(defaultTextColor)backColor(defaultBackColor)
 68         {
 69         }
 70         private void Put(char cconst ColorCharOutputMethod& writeOutputChar)
 71         {
 72             switch (state)
 73             {
 74                 case 0:
 75                 {
 76                     if (c == '\033')
 77                     {
 78                         state = 1;
 79                     }
 80                     else
 81                     {
 82                         Emit(cwriteOutputChar);
 83                     }
 84                     break;
 85                 }
 86                 case 1:
 87                 {
 88                     if (c == '[')
 89                     {
 90                         state = 2;
 91                     }
 92                     else if (c == '\033')
 93                     {
 94                         Emit('\033'writeOutputChar);
 95                     }
 96                     else
 97                     {
 98                         Emit('\033'writeOutputChar);
 99                         Emit(cwriteOutputChar);
100                         state = 0;
101                     }
102                     break;
103                 }
104                 case 2:
105                 {
106                     switch (c)
107                     {
108                         case '3': state = 3; break;
109                         case '9': state = 9; break;
110                         case '4': state = 4; break;
111                         case '1': state = 10; break;
112                         case '0': state = 999; break;
113                         default:
114                         {
115                             Emit('\033'writeOutputChar);
116                             Emit('['writeOutputChar);
117                             Emit(cwriteOutputChar);
118                             state = 0;
119                             break;
120                         }
121                     }
122                     break;
123                 }
124                 case 3:
125                 {
126                     switch (c)
127                     {
128                         case '0': currentTextColor = Color.Constant.black; state = 1000; break;
129                         case '1': currentTextColor = Color.Constant.darkRed; state = 1001; break;
130                         case '2': currentTextColor = Color.Constant.darkGreen; state = 1002; break;
131                         case '3': currentTextColor = Color.Constant.darkYellow; state = 1003; break;
132                         case '4': currentTextColor = Color.Constant.darkBlue; state = 1004; break;
133                         case '5': currentTextColor = Color.Constant.darkGray; state = 1005; break;
134                         case '6': currentTextColor = Color.Constant.darkCyan; state = 1006; break;
135                         case '7': currentTextColor = Color.Constant.gray; state = 1007; break;
136                         default:
137                         {
138                             Emit('\033'writeOutputChar);
139                             Emit('['writeOutputChar);
140                             Emit('3'writeOutputChar);
141                             Emit(cwriteOutputChar);
142                             state = 0;
143                             break;
144                         }
145                     }
146                     break;
147                 }
148                 case 9:
149                 {
150                     switch (c)
151                     {
152                         case '1': currentTextColor = Color.Constant.red; state = 9001; break;
153                         case '2': currentTextColor = Color.Constant.green; state = 9002; break;
154                         case '3': currentTextColor = Color.Constant.yellow; state = 9003; break;
155                         case '4': currentTextColor = Color.Constant.blue; state = 9004; break;
156                         case '5': currentTextColor = Color.Constant.magenta; state = 9005; break;
157                         case '6': currentTextColor = Color.Constant.cyan; state = 9006; break;
158                         case '7': currentTextColor = Color.Constant.white; state = 9007; break;
159                         default:
160                         {
161                             Emit('\033'writeOutputChar);
162                             Emit('['writeOutputChar);
163                             Emit('9'writeOutputChar);
164                             Emit(cwriteOutputChar);
165                             state = 0;
166                             break;
167                         }
168                     }
169                     break;
170                 }
171                 case 4:
172                 {
173                     switch (c)
174                     {
175                         case '0': currentBackColor = Color.Constant.black; state = 4000; break;
176                         case '1': currentBackColor = Color.Constant.darkRed; state = 4001; break;
177                         case '2': currentBackColor = Color.Constant.darkGreen; state = 4002; break;
178                         case '3': currentBackColor = Color.Constant.darkYellow; state = 4003; break;
179                         case '4': currentBackColor = Color.Constant.darkBlue; state = 4004; break;
180                         case '5': currentBackColor = Color.Constant.darkGray; state = 4005; break;
181                         case '6': currentBackColor = Color.Constant.darkCyan; state = 4006; break;
182                         case '7': currentBackColor = Color.Constant.gray; state = 4007; break;
183                         default:
184                         {
185                             Emit('\033'writeOutputChar);
186                             Emit('['writeOutputChar);
187                             Emit('4'writeOutputChar);
188                             Emit(cwriteOutputChar);
189                             state = 0;
190                             break;
191                         }
192                     }
193                     break;
194                 }
195                 case 10:
196                 {
197                     if (c == '0')
198                     {
199                         state = 100;
200                     }
201                     else
202                     {
203                         Emit('\033'writeOutputChar);
204                         Emit('['writeOutputChar);
205                         Emit('1'writeOutputChar);
206                         Emit(cwriteOutputChar);
207                         state = 0;
208                         break;
209                     }
210                     break;
211                 }
212                 case 100:
213                 {
214                     switch (c)
215                     {
216                         case '1': currentBackColor = Color.Constant.red; state = 10001; break;
217                         case '2': currentBackColor = Color.Constant.green; state = 10002; break;
218                         case '3': currentBackColor = Color.Constant.yellow; state = 10003; break;
219                         case '4': currentBackColor = Color.Constant.blue; state = 10004; break;
220                         case '5': currentBackColor = Color.Constant.magenta; state = 10005; break;
221                         case '6': currentBackColor = Color.Constant.cyan; state = 10006; break;
222                         case '7': currentBackColor = Color.Constant.white; state = 10007; break;
223                         default:
224                         {
225                             Emit('\033'writeOutputChar);
226                             Emit('['writeOutputChar);
227                             Emit('1'writeOutputChar);
228                             Emit('0'writeOutputChar);
229                             Emit(cwriteOutputChar);
230                             state = 0;
231                             break;
232                         }
233                     }
234                     break;
235                 }
236                 case 999:
237                 {
238                     if (c == 'm')
239                     {
240                         ResetAttributes();
241                     }
242                     else
243                     {
244                         Emit('\033'writeOutputChar);
245                         Emit('['writeOutputChar);
246                         Emit('0'writeOutputChar);
247                         Emit(cwriteOutputChar);
248                         break;
249                     }
250                     state = 0;
251                     break;
252                 }
253                 case 1000:
254                 {
255                     if (c == 'm')
256                     {
257                         SetAttributes();
258                     }
259                     else
260                     {
261                         Emit('\033'writeOutputChar);
262                         Emit('['writeOutputChar);
263                         Emit('3'writeOutputChar);
264                         Emit('0'writeOutputChar);
265                         Emit(cwriteOutputChar);
266                     }
267                     state = 0;
268                     break;
269                 }
270                 case 1001:
271                 {
272                     if (c == 'm')
273                     {
274                         SetAttributes();
275                     }
276                     else
277                     {
278                         Emit('\033'writeOutputChar);
279                         Emit('['writeOutputChar);
280                         Emit('3'writeOutputChar);
281                         Emit('1'writeOutputChar);
282                         Emit(cwriteOutputChar);
283                     }
284                     state = 0;
285                     break;
286                 }
287                 case 1002:
288                 {
289                     if (c == 'm')
290                     {
291                         SetAttributes();
292                     }
293                     else
294                     {
295                         Emit('\033'writeOutputChar);
296                         Emit('['writeOutputChar);
297                         Emit('3'writeOutputChar);
298                         Emit('2'writeOutputChar);
299                         Emit(cwriteOutputChar);
300                     }
301                     state = 0;
302                     break;
303                 }
304                 case 1003:
305                 {
306                     if (c == 'm')
307                     {
308                         SetAttributes();
309                     }
310                     else
311                     {
312                         Emit('\033'writeOutputChar);
313                         Emit('['writeOutputChar);
314                         Emit('3'writeOutputChar);
315                         Emit('3'writeOutputChar);
316                         Emit(cwriteOutputChar);
317                     }
318                     state = 0;
319                     break;
320                 }
321                 case 1004:
322                 {
323                     if (c == 'm')
324                     {
325                         SetAttributes();
326                     }
327                     else
328                     {
329                         Emit('\033'writeOutputChar);
330                         Emit('['writeOutputChar);
331                         Emit('3'writeOutputChar);
332                         Emit('4'writeOutputChar);
333                         Emit(cwriteOutputChar);
334                     }
335                     state = 0;
336                     break;
337                 }
338                 case 1005:
339                 {
340                     if (c == 'm')
341                     {
342                         SetAttributes();
343                     }
344                     else
345                     {
346                         Emit('\033'writeOutputChar);
347                         Emit('['writeOutputChar);
348                         Emit('3'writeOutputChar);
349                         Emit('5'writeOutputChar);
350                         Emit(cwriteOutputChar);
351                     }
352                     state = 0;
353                     break;
354                 }
355                 case 1006:
356                 {
357                     if (c == 'm')
358                     {
359                         SetAttributes();
360                     }
361                     else
362                     {
363                         Emit('\033'writeOutputChar);
364                         Emit('['writeOutputChar);
365                         Emit('3'writeOutputChar);
366                         Emit('6'writeOutputChar);
367                         Emit(cwriteOutputChar);
368                     }
369                     state = 0;
370                     break;
371                 }
372                 case 1007:
373                 {
374                     if (c == 'm')
375                     {
376                         SetAttributes();
377                     }
378                     else
379                     {
380                         Emit('\033'writeOutputChar);
381                         Emit('['writeOutputChar);
382                         Emit('3'writeOutputChar);
383                         Emit('7'writeOutputChar);
384                         Emit(cwriteOutputChar);
385                     }
386                     state = 0;
387                     break;
388                 }
389                 case 9001:
390                 {
391                     if (c == 'm')
392                     {
393                         SetAttributes();
394                     }
395                     else
396                     {
397                         Emit('\033'writeOutputChar);
398                         Emit('['writeOutputChar);
399                         Emit('9'writeOutputChar);
400                         Emit('1'writeOutputChar);
401                         Emit(cwriteOutputChar);
402                     }
403                     state = 0;
404                     break;
405                 }
406                 case 9002:
407                 {
408                     if (c == 'm')
409                     {
410                         SetAttributes();
411                     }
412                     else
413                     {
414                         Emit('\033'writeOutputChar);
415                         Emit('['writeOutputChar);
416                         Emit('9'writeOutputChar);
417                         Emit('2'writeOutputChar);
418                         Emit(cwriteOutputChar);
419                     }
420                     state = 0;
421                     break;
422                 }
423                 case 9003:
424                 {
425                     if (c == 'm')
426                     {
427                         SetAttributes();
428                     }
429                     else
430                     {
431                         Emit('\033'writeOutputChar);
432                         Emit('['writeOutputChar);
433                         Emit('9'writeOutputChar);
434                         Emit('3'writeOutputChar);
435                         Emit(cwriteOutputChar);
436                     }
437                     state = 0;
438                     break;
439                 }
440                 case 9004:
441                 {
442                     if (c == 'm')
443                     {
444                         SetAttributes();
445                     }
446                     else
447                     {
448                         Emit('\033'writeOutputChar);
449                         Emit('['writeOutputChar);
450                         Emit('9'writeOutputChar);
451                         Emit('4'writeOutputChar);
452                         Emit(cwriteOutputChar);
453                     }
454                     state = 0;
455                     break;
456                 }
457                 case 9005:
458                 {
459                     if (c == 'm')
460                     {
461                         SetAttributes();
462                     }
463                     else
464                     {
465                         Emit('\033'writeOutputChar);
466                         Emit('['writeOutputChar);
467                         Emit('9'writeOutputChar);
468                         Emit('5'writeOutputChar);
469                         Emit(cwriteOutputChar);
470                     }
471                     state = 0;
472                     break;
473                 }
474                 case 9006:
475                 {
476                     if (c == 'm')
477                     {
478                         SetAttributes();
479                     }
480                     else
481                     {
482                         Emit('\033'writeOutputChar);
483                         Emit('['writeOutputChar);
484                         Emit('9'writeOutputChar);
485                         Emit('6'writeOutputChar);
486                         Emit(cwriteOutputChar);
487                     }
488                     state = 0;
489                     break;
490                 }
491                 case 9007:
492                 {
493                     if (c == 'm')
494                     {
495                         SetAttributes();
496                     }
497                     else
498                     {
499                         Emit('\033'writeOutputChar);
500                         Emit('['writeOutputChar);
501                         Emit('9'writeOutputChar);
502                         Emit('7'writeOutputChar);
503                         Emit(cwriteOutputChar);
504                     }
505                     state = 0;
506                     break;
507                 }
508                 case 4000:
509                 {
510                     if (c == 'm')
511                     {
512                         SetAttributes();
513                     }
514                     else
515                     {
516                         Emit('\033'writeOutputChar);
517                         Emit('['writeOutputChar);
518                         Emit('4'writeOutputChar);
519                         Emit('0'writeOutputChar);
520                         Emit(cwriteOutputChar);
521                     }
522                     state = 0;
523                     break;
524                 }
525                 case 4001:
526                 {
527                     if (c == 'm')
528                     {
529                         SetAttributes();
530                     }
531                     else
532                     {
533                         Emit('\033'writeOutputChar);
534                         Emit('['writeOutputChar);
535                         Emit('4'writeOutputChar);
536                         Emit('1'writeOutputChar);
537                         Emit(cwriteOutputChar);
538                     }
539                     state = 0;
540                     break;
541                 }
542                 case 4002:
543                 {
544                     if (c == 'm')
545                     {
546                         SetAttributes();
547                     }
548                     else
549                     {
550                         Emit('\033'writeOutputChar);
551                         Emit('['writeOutputChar);
552                         Emit('4'writeOutputChar);
553                         Emit('2'writeOutputChar);
554                         Emit(cwriteOutputChar);
555                     }
556                     state = 0;
557                     break;
558                 }
559                 case 4003:
560                 {
561                     if (c == 'm')
562                     {
563                         SetAttributes();
564                     }
565                     else
566                     {
567                         Emit('\033'writeOutputChar);
568                         Emit('['writeOutputChar);
569                         Emit('4'writeOutputChar);
570                         Emit('3'writeOutputChar);
571                         Emit(cwriteOutputChar);
572                     }
573                     state = 0;
574                     break;
575                 }
576                 case 4004:
577                 {
578                     if (c == 'm')
579                     {
580                         SetAttributes();
581                     }
582                     else
583                     {
584                         Emit('\033'writeOutputChar);
585                         Emit('['writeOutputChar);
586                         Emit('4'writeOutputChar);
587                         Emit('4'writeOutputChar);
588                         Emit(cwriteOutputChar);
589                     }
590                     state = 0;
591                     break;
592                 }
593                 case 4005:
594                 {
595                     if (c == 'm')
596                     {
597                         SetAttributes();
598                     }
599                     else
600                     {
601                         Emit('\033'writeOutputChar);
602                         Emit('['writeOutputChar);
603                         Emit('4'writeOutputChar);
604                         Emit('5'writeOutputChar);
605                         Emit(cwriteOutputChar);
606                     }
607                     state = 0;
608                     break;
609                 }
610                 case 4006:
611                 {
612                     if (c == 'm')
613                     {
614                         SetAttributes();
615                     }
616                     else
617                     {
618                         Emit('\033'writeOutputChar);
619                         Emit('['writeOutputChar);
620                         Emit('4'writeOutputChar);
621                         Emit('6'writeOutputChar);
622                         Emit(cwriteOutputChar);
623                     }
624                     state = 0;
625                     break;
626                 }
627                 case 4007:
628                 {
629                     if (c == 'm')
630                     {
631                         SetAttributes();
632                     }
633                     else
634                     {
635                         Emit('\033'writeOutputChar);
636                         Emit('['writeOutputChar);
637                         Emit('4'writeOutputChar);
638                         Emit('7'writeOutputChar);
639                         Emit(cwriteOutputChar);
640                     }
641                     state = 0;
642                     break;
643                 }
644                 case 10001:
645                 {
646                     if (c == 'm')
647                     {
648                         SetAttributes();
649                     }
650                     else
651                     {
652                         Emit('\033'writeOutputChar);
653                         Emit('['writeOutputChar);
654                         Emit('1'writeOutputChar);
655                         Emit('0'writeOutputChar);
656                         Emit('1'writeOutputChar);
657                         Emit(cwriteOutputChar);
658                     }
659                     state = 0;
660                     break;
661                 }
662                 case 10002:
663                 {
664                     if (c == 'm')
665                     {
666                         SetAttributes();
667                     }
668                     else
669                     {
670                         Emit('\033'writeOutputChar);
671                         Emit('['writeOutputChar);
672                         Emit('1'writeOutputChar);
673                         Emit('0'writeOutputChar);
674                         Emit('2'writeOutputChar);
675                         Emit(cwriteOutputChar);
676                     }
677                     state = 0;
678                     break;
679                 }
680                 case 10003:
681                 {
682                     if (c == 'm')
683                     {
684                         SetAttributes();
685                     }
686                     else
687                     {
688                         Emit('\033'writeOutputChar);
689                         Emit('['writeOutputChar);
690                         Emit('1'writeOutputChar);
691                         Emit('0'writeOutputChar);
692                         Emit('3'writeOutputChar);
693                         Emit(cwriteOutputChar);
694                     }
695                     state = 0;
696                     break;
697                 }
698                 case 10004:
699                 {
700                     if (c == 'm')
701                     {
702                         SetAttributes();
703                     }
704                     else
705                     {
706                         Emit('\033'writeOutputChar);
707                         Emit('['writeOutputChar);
708                         Emit('1'writeOutputChar);
709                         Emit('0'writeOutputChar);
710                         Emit('4'writeOutputChar);
711                         Emit(cwriteOutputChar);
712                     }
713                     state = 0;
714                     break;
715                 }
716                 case 10005:
717                 {
718                     if (c == 'm')
719                     {
720                         SetAttributes();
721                     }
722                     else
723                     {
724                         Emit('\033'writeOutputChar);
725                         Emit('['writeOutputChar);
726                         Emit('1'writeOutputChar);
727                         Emit('0'writeOutputChar);
728                         Emit('5'writeOutputChar);
729                         Emit(cwriteOutputChar);
730                     }
731                     state = 0;
732                     break;
733                 }
734                 case 10006:
735                 {
736                     if (c == 'm')
737                     {
738                         SetAttributes();
739                     }
740                     else
741                     {
742                         Emit('\033'writeOutputChar);
743                         Emit('['writeOutputChar);
744                         Emit('1'writeOutputChar);
745                         Emit('0'writeOutputChar);
746                         Emit('6'writeOutputChar);
747                         Emit(cwriteOutputChar);
748                     }
749                     state = 0;
750                     break;
751                 }
752                 case 10007:
753                 {
754                     if (c == 'm')
755                     {
756                         SetAttributes();
757                     }
758                     else
759                     {
760                         Emit('\033'writeOutputChar);
761                         Emit('['writeOutputChar);
762                         Emit('1'writeOutputChar);
763                         Emit('0'writeOutputChar);
764                         Emit('7'writeOutputChar);
765                         Emit(cwriteOutputChar);
766                     }
767                     state = 0;
768                     break;
769                 }
770             }
771         }
772         private void Emit(char cconst ColorCharOutputMethod& writeOutputChar)
773         {
774             unicodeEngine.Put(cast<byte>(c));
775             if (unicodeEngine.ResultReady())
776             {
777                 writeOutputChar(textColorbackColorhandleunicodeEngine.Get());
778             }
779         }
780         private nothrow void SetAttributes()
781         {
782             textColor = currentTextColor;
783             backColor = currentBackColor;
784         }
785         private nothrow void ResetAttributes()
786         {
787             textColor = defaultTextColor;
788             backColor = defaultBackColor;
789         }
790         private static UniquePtr<AnsiEngine> out;
791         private static UniquePtr<AnsiEngine> error;
792         private int handle;
793         private int state;
794         private System.Unicode.UnicodeEngine unicodeEngine;
795         private Color.Constant defaultTextColor;
796         private Color.Constant defaultBackColor;
797         private Color.Constant currentTextColor;
798         private Color.Constant currentBackColor;
799         private Color.Constant textColor;
800         private Color.Constant backColor;
801     }
802 }