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