Shellminator  V1.2.0
Simple Terminal
Loading...
Searching...
No Matches
Shellminator.cpp
Go to the documentation of this file.
1/*
2 * Created on Aug 10 2020
3 *
4 * Copyright (c) 2020 - Daniel Hajnal
5 * hajnal.daniel96@gmail.com
6 * This file is part of the Shellminator project.
7 * Modified 2022.05.08
8*/
9
10/*
11MIT License
12
13Copyright (c) 2020 Daniel Hajnal
14
15Permission is hereby granted, free of charge, to any person obtaining a copy
16of this software and associated documentation files (the "Software"), to deal
17in the Software without restriction, including without limitation the rights
18to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19copies of the Software, and to permit persons to whom the Software is
20furnished to do so, subject to the following conditions:
21
22The above copyright notice and this permission notice shall be included in all
23copies or substantial portions of the Software.
24
25THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31SOFTWARE.
32*/
33
34#include "Shellminator.hpp"
35
36#ifdef __has_include
37 #if __has_include ("Commander-API.hpp")
38 #include "Commander-API.hpp"
39 #endif
40#endif
41
42#ifdef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE
43#include <WebSocketsServer.h>
44#endif
45
47
48#ifdef SHELLMINATOR_USE_WIFI_CLIENT
49
50#ifdef ESP32
51 #define CLIENT_STATE client.connected()
52#endif
53
54#ifdef ESP8266
55 #define CLIENT_STATE ( client.status() == ESTABLISHED )
56#endif
57
58const uint8_t Shellminator::TELNET_IAC_DONT_LINEMODE[] = { 255, 254, 34 };
59const uint8_t Shellminator::TELNET_IAC_WILL_ECHO[] = { 255, 251, 1 };
60const uint8_t Shellminator::TELNET_IAC_DONT_ECHO[] = { 255, 254, 1 };
61const uint8_t Shellminator::TELNET_IAC_WILL_SUPRESS_GO_AHEAD[] = { 255, 251, 3 };
62const uint8_t Shellminator::TELNET_IAC_DO_SUPRESS_GO_AHEAD[] = { 255, 253, 3 };
63
64Shellminator::Shellminator( WiFiServer *server_p ){
65
66 server = server_p;
67
68 // It has to be zero. We dont want to process any garbage.
69 cmd_buff_cntr = 0;
70
71 // This has to be 1 minimum, because the 0th element is used for the incoming data.
72 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
73 cmd_buff_dim = 1;
74
75 // Just in case terminate the begining of the buffer
76 cmd_buff[ 0 ][ 0 ] = '\0';
77
78 // Because we did not specified the execution function, we have to make it a NULL
79 // pointer to make it detectable.
80 execution_fn = NULL;
81
82}
83
84Shellminator::Shellminator( WiFiServer *server_p, void( *execution_fn_p )( char* ) ){
85
86 server = server_p;
87
88 // It has to be zero. We dont want to process any garbage.
89 cmd_buff_cntr = 0;
90
91 // This has to be 1 minimum, because the 0th element is used for the incoming data.
92 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
93 cmd_buff_dim = 1;
94
95 // Just in case terminate the begining of the buffer
96 cmd_buff[ 0 ][ 0 ] = '\0';
97
98 // passing execution_fn_p to execution_fn
99 execution_fn = execution_fn_p;
100
101}
102
104
105 if( server ){
106
107 server -> begin();
108 server -> setNoDelay( true );
109
110 }
111
112}
113
115
116 if( server ){
117
118 server -> stop();
119
120 }
121
122}
123
124void Shellminator::setClientTimeout( uint16_t clientTimeout_p ){
125
126 clientTimeout = clientTimeout_p;
127
128}
129
130#endif
131
132#ifdef __AVR__
133
134const char Shellminator::helpText[] PROGMEM = {
135 "\r\n"
136 "\033[1;31m----\033[1;32m Shortcut Keys \033[1;31m----\033[0;37m\r\n"
137 "\r\n"
138 "\033[1;31mCtrl-A\033[1;32m : Jumps the cursor to the beginning of the line.\r\n"
139 "\033[1;31mCtrl-E\033[1;32m : Jumps the cursor to the end of the line.\r\n"
140 "\033[1;31mCtrl-D\033[1;32m : Log Out.\r\n"
141 "\033[1;31mCtrl-R\033[1;32m : Reverse-i-search.\r\n"
142 "\033[1;31mPg-Up\033[1;32m : History search backwards and auto completion.\r\n"
143 "\033[1;31mPg-Down\033[1;32m: History search forward and auto completion.\r\n"
144 "\033[1;31mHome\033[1;32m : Jumps the cursor to the beginning of the line.\r\n"
145 "\033[1;31mEnd\033[1;32m : Jumps the cursor to the end of the line.\r\n"
146 "\r\n"
147};
148
149#else
150
151const char Shellminator::helpText[] = {
152 "\r\n"
153 "\033[1;31m----\033[1;32m Shortcut Keys \033[1;31m----\033[0;37m\r\n"
154 "\r\n"
155 "\033[1;31mCtrl-A\033[1;32m : Jumps the cursor to the beginning of the line.\r\n"
156 "\033[1;31mCtrl-E\033[1;32m : Jumps the cursor to the end of the line.\r\n"
157 "\033[1;31mCtrl-D\033[1;32m : Log Out.\r\n"
158 "\033[1;31mCtrl-R\033[1;32m : Reverse-i-search.\r\n"
159 "\033[1;31mPg-Up\033[1;32m : History search backwards and auto completion.\r\n"
160 "\033[1;31mPg-Down\033[1;32m: History search forward and auto completion.\r\n"
161 "\033[1;31mHome\033[1;32m : Jumps the cursor to the beginning of the line.\r\n"
162 "\033[1;31mEnd\033[1;32m : Jumps the cursor to the end of the line.\r\n"
163 "\r\n"
164};
165
166#endif
167
168#ifdef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE
169
170Shellminator::Shellminator( WebSocketsServer *wsServer_p, uint8_t serverID_p ){
171
172 wsServer = wsServer_p;
173 serverID = serverID_p;
174 webSocketChannel.select( wsServer, serverID );
175 webSocketChannel.setTimeout( 10 );
176 channel = &webSocketChannel;
177
178 // It has to be zero. We dont want to process any garbage.
179 cmd_buff_cntr = 0;
180
181 // This has to be 1 minimum, because the 0th element is used for the incoming data.
182 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
183 cmd_buff_dim = 1;
184
185 // Just in case terminate the begining of the buffer
186 cmd_buff[ 0 ][ 0 ] = '\0';
187
188 // Because we did not specified the execution function, we have to make it a NULL
189 // pointer to make it detectable.
190 execution_fn = NULL;
191
192}
193
194Shellminator::Shellminator( WebSocketsServer *wsServer_p ){
195
196 wsServer = wsServer_p;
197 serverID = 0;
198 webSocketChannel.select( wsServer, serverID );
199 webSocketChannel.setTimeout( 10 );
200 channel = &webSocketChannel;
201
202 // It has to be zero. We dont want to process any garbage.
203 cmd_buff_cntr = 0;
204
205 // This has to be 1 minimum, because the 0th element is used for the incoming data.
206 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
207 cmd_buff_dim = 1;
208
209 // Just in case terminate the begining of the buffer
210 cmd_buff[ 0 ][ 0 ] = '\0';
211
212 // Because we did not specified the execution function, we have to make it a NULL
213 // pointer to make it detectable.
214 execution_fn = NULL;
215
216}
217
218Shellminator::Shellminator( WebSocketsServer *wsServer_p, uint8_t serverID_p, void( *execution_fn_p )( char* ) ){
219
220 wsServer = wsServer_p;
221 serverID = serverID_p;
222 webSocketChannel.select( wsServer, serverID );
223 channel = &webSocketChannel;
224
225 // It has to be zero. We dont want to process any garbage.
226 cmd_buff_cntr = 0;
227
228 // This has to be 1 minimum, because the 0th element is used for the incoming data.
229 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
230 cmd_buff_dim = 1;
231
232 // Just in case terminate the begining of the buffer
233 cmd_buff[ 0 ][ 0 ] = '\0';
234
235 // passing execution_fn_p to execution_fn
236 execution_fn = execution_fn_p;
237
238}
239
240void Shellminator::webSocketPush( uint8_t data ){
241 webSocketChannel.push( data );
242}
243
244void Shellminator::webSocketPush( uint8_t* data, size_t size ){
245 webSocketChannel.push( data, size );
246}
247
248void Shellminator::websocketDisconnect(){
249
250 if( wsServer ){
251 wsServer -> disconnect( serverID );
252 }
253
254}
255
256#endif
257
258Shellminator::Shellminator( Stream *stream_p ){
259
260 channel = stream_p;
261
262 // It has to be zero. We dont want to process any garbage.
263 cmd_buff_cntr = 0;
264
265 // This has to be 1 minimum, because the 0th element is used for the incoming data.
266 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
267 cmd_buff_dim = 1;
268
269 // Just in case terminate the begining of the buffer
270 cmd_buff[ 0 ][ 0 ] = '\0';
271
272 // Because we did not specified the execution function, we have to make it a NULL
273 // pointer to make it detectable.
274 execution_fn = NULL;
275
276}
277
278Shellminator::Shellminator( Stream *stream_p, void( *execution_fn_p )( char* ) ){
279
280 channel = stream_p;
281
282 // It has to be zero. We dont want to process any garbage.
283 cmd_buff_cntr = 0;
284
285 // This has to be 1 minimum, because the 0th element is used for the incoming data.
286 // The maximum value has to be ( SHELLMINATOR_BUFF_DIM - 1 )
287 cmd_buff_dim = 1;
288
289 // Just in case terminate the begining of the buffer
290 cmd_buff[ 0 ][ 0 ] = '\0';
291
292 // Because we did not specified the execution function, we have to make it a NULL
293 // pointer to make it detectable.
294 execution_fn = execution_fn_p;
295
296}
297
298void Shellminator::setBannerText( char* banner_p ){
299
300 // Copy the content from banner_p to banner. Because strncpy we can be sure that it wont overflow.
301 strncpy( banner, banner_p, SHELLMINATOR_BANNER_LEN );
302
303 // Just in case close the string
304 banner[ SHELLMINATOR_BANNER_LEN - 1 ] = '\0';
305
306}
307
308void Shellminator::setBannerText( const char* banner_p ){
309
310 // Copy the content from banner_p to banner. Because strncpy we can be sure that it wont overflow.
311 strncpy( banner, banner_p, SHELLMINATOR_BANNER_LEN );
312
313 // Just in case close the string
314 banner[ SHELLMINATOR_BANNER_LEN - 1 ] = '\0';
315
316}
317
318void Shellminator::setBannerPathText( char* bannerPath_p ){
319
320 // Copy the content from bannerPath_p to bannerPath. Because strncpy we can be sure that it wont overflow.
321 strncpy( bannerPath, bannerPath_p, SHELLMINATOR_BANNER_PATH_LEN );
322
323 // Just in case close the string
324 banner[ SHELLMINATOR_BANNER_PATH_LEN - 1 ] = '\0';
325
326}
327
328void Shellminator::setBannerPathText( const char* bannerPath_p ){
329
330 // Copy the content from bannerPath_p to bannerPath. Because strncpy we can be sure that it wont overflow.
331 strncpy( bannerPath, bannerPath_p, SHELLMINATOR_BANNER_PATH_LEN );
332
333 // Just in case close the string
334 banner[ SHELLMINATOR_BANNER_PATH_LEN - 1 ] = '\0';
335
336}
337
338void Shellminator::attachLogo( char* logo_p ){
339
340 logo = logo_p;
341
342}
343
344#ifdef __AVR__
345void Shellminator::attachLogo( __FlashStringHelper * progmemLogo_p ){
346
347 progmemLogo = progmemLogo_p;
348
349}
350#endif
351
352void Shellminator::attachLogo( const char* logo_p ){
353
354 logo = (char*)logo_p;
355
356}
357
358void Shellminator::addExecFunc( void( *execution_fn_p )( char* ) ){
359
360 // passing execution_fn_p to execution_fn
361 execution_fn = execution_fn_p;
362
363}
364
366
367 // explanation can be found here: http://braun-home.net/michael/info/misc/VT100_commands.htm
368 channel -> write( 27 ); // ESC character( decimal 27 )
369 channel -> print( (const char*)"[H" ); // VT100 Home command
370 channel -> write( 27 ); // ESC character( decimal 27 )
371 channel -> print( (const char*)"[J" ); // VT100 screen erase command
372
373}
374
376
377 lastBannerSize = 0;
378
379 // Sets the terminal style to bold and the color to green.
380 // You can change it if you like. In my opinion the most
381 // useful is the invisible one :)
383
384 // Print the banner text and save it's size.
385 lastBannerSize += channel -> print( banner );
386
387 // Sets the terminal style to regular and the color to white.
389
390 lastBannerSize += channel -> print( ':' );
391
393
394 lastBannerSize += channel -> print( bannerPath );
395
397
398 lastBannerSize += channel -> print( ' ' );
399
400}
401
403
404 uint32_t i;
405 uint32_t firstBuffDim = 1;
406 uint32_t index;
407
408 // Check the first valid command in the buffer
409 for( i = 1; i < SHELLMINATOR_BUFF_DIM; i++ ){
410
411 // If it's found, store it's index to firstBuffDim
412 if( cmd_buff[ i ][ 0 ] == '\0' ){
413 firstBuffDim = i - 1;
414 break;
415 }
416
417 }
418
419 // If the history is empty, we can return.
420 if( firstBuffDim == 0 ){
421 return;
422 }
423
424 // If the history is full, we have to protect
425 // firstBuffDim variable.
426 if( i >= SHELLMINATOR_BUFF_DIM ){
427 firstBuffDim = SHELLMINATOR_BUFF_DIM - 1;
428 }
429
430 // Print the history.
431 for( i = firstBuffDim; i > 0; i-- ){
432
433 index = firstBuffDim - i + 1;
434
435 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
436
437 sprintf( acceleratorBuffer, " \033[1;35m%3d \033[0;37m%s\r\n", index, cmd_buff[ i ] );
438 channel -> print( acceleratorBuffer );
439
440 #else
441
442
443 channel -> print( ' ' );
444 channel -> print( ' ' );
445
446 // It is used to ident digits.
447 if( index < 10 ){
448
449 channel -> print( ' ' );
450 channel -> print( ' ' );
451
452 }
453
454 // It is used to ident digits.
455 else if( index < 100 ){
456
457 channel -> print( ' ' );
458
459 }
460
461 // Print the index and the command.
463 channel -> print( index );
465 channel -> print( ' ' );
466 channel -> print( ' ' );
467 channel -> println( cmd_buff[ i ] );
468
469 #endif
470
471 }
472
473}
474
476
477 #ifdef __AVR__
478
479 uint32_t i;
480
481 for( i = 0; i < strlen_P( helpText ); i++ ){
482
483 char c = pgm_read_byte_near( helpText + i );
484 channel -> print( c );
485
486 }
487
488 #else
489
490 channel -> print( helpText );
491
492 #endif
493
494 #ifdef COMMANDER_API_VERSION
495
496 if( commander != NULL ){
497
498 commander -> printHelp( channel );
499
500 }
501
502 #endif
503
504}
505
506void Shellminator::begin( char* banner_p ) {
507
508 // Copy the content from banner_p to banner. Because strncpy we can be sure that it wont overflow.
509 strncpy( banner, banner_p, SHELLMINATOR_BANNER_LEN );
510
511 // Just in case close the string
512 banner[ SHELLMINATOR_BANNER_LEN - 1 ] = '\0';
513
514 // Draw the startup logo.
515 drawLogo();
516
517 // Print the banner message.
518 printBanner();
519
520}
521
522void Shellminator::begin( const char* banner_p ) {
523
524 // Copy the content from banner_p to banner. Because strncpy we can be sure that it wont overflow.
525 strncpy( banner, banner_p, SHELLMINATOR_BANNER_LEN );
526
527 // Just in case close the string
528 banner[ SHELLMINATOR_BANNER_LEN - 1 ] = '\0';
529
530 // Set the terminal color and style to the defined settings for the logo
532
533 // Draw the startup logo.
534 drawLogo();
535
536 // Print the banner message.
537 printBanner();
538
539}
540
542
543 // Send a simple backspace combo to the serial port
544 channel -> print( (const char*)"\b \b" );
545
546}
547
548void Shellminator::redrawLine(){
549
550 // General counter variable
551 #ifdef COMMANDER_API_VERSION
552
553 uint32_t i;
554
555 #endif
556
557 int32_t j = -1;
558
559 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
560
561 if( inSearch ){
562
563 redrawHistorySearch();
564 return;
565
566 }
567
568 #endif
569
570
571 if( cmd_buff_cntr > SHELLMINATOR_BUFF_LEN ){
572
573 cmd_buff_cntr = SHELLMINATOR_BUFF_LEN;
574
575 }
576
577 // Terminate the command at the cmd_buff_cntr
578 // to not print out the previous command's data.
579 cmd_buff[ 0 ][ cmd_buff_cntr ] = '\0';
580
581 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
582
583 acceleratorBufferPtr = acceleratorBuffer;
584 // acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[%dC\033[0K", lastBannerSize );
585 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[1;32m%s\033[1;37m:\033[1;34m%s\033[0;37m \033[0K", banner, bannerPath );
586
587 #else
588
589 // Return to the beginning of the line and print the banner
590 // then the command buffer will be printed (with colors)
591 channel -> print( '\r' );
592
593 /*
594 channel -> write( 27 );
595 channel -> print( '[' );
596 channel -> print( lastBannerSize );
597 channel -> print( 'C' );
598 */
599
600 printBanner();
601
602 channel -> write( 27 );
603 channel -> print( "[0K" );
604
605 #endif
606
607 #ifdef COMMANDER_API_VERSION
608 //#ifdef FALSE
609
610 // If the command is found in Commander's API-tree
611 // it will be highlighted.
612 if( commandFound ){
613
614 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
615
616 setTerminalCharacterColor( acceleratorBufferPtr, BOLD, GREEN );
617 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
618
619 #else
620
622
623 #endif
624
625 for( i = 0; i < cmd_buff_cntr; i++ ){
626
627 // If a space character is found, we have to change
628 // back the color to white for the arguments.
629 if( cmd_buff[ 0 ][ i ] == ' ' ){
630
631 j = i;
632 cmd_buff[ 0 ][ i ] = '\0';
633 break;
634
635 }
636
637 }
638
639 }
640
641 else{
642
643 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
644
645 setTerminalCharacterColor( acceleratorBufferPtr, REGULAR, WHITE );
646 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
647
648 #else
649
651
652 #endif
653
654 }
655
656 #endif
657
658 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
659
660 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "%s", (char*) &cmd_buff[ 0 ] );
661
662 #else
663
664 channel -> print( (char*) &cmd_buff[ 0 ] );
665
666 #endif
667
668 if( ( j >= 0 ) ){
669
670 cmd_buff[ 0 ][ j ] = ' ';
671
672 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
673
674 setTerminalCharacterColor( acceleratorBufferPtr, REGULAR, WHITE );
675 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
676 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "%s", (char*) &cmd_buff[ 0 ][ j ] );
677
678 #else
679
681 channel -> print( (char*) &cmd_buff[ 0 ][ j ] );
682
683 #endif
684
685 }
686
687 // After all the buffer is out, we can "kill" the rest of the line
688 // (clear the line from cursor to the end)
689 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
690
691 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\033[0K" );
692
693 #else
694
695 channel -> write( 27 );
696 channel -> print( "[0K" );
697
698 #endif
699
700
701
702 if( cmd_buff_cntr > cursor ){
703
704
705 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
706
707 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\033[%dD", uint8_t( cmd_buff_cntr - cursor ) );
708
709 #else
710
711 channel -> write( 27 ); // ESC character( decimal 27 )
712 channel -> print( '[' ); // VT100 Cursor command.
713 channel -> print( uint8_t( cmd_buff_cntr - cursor ) ); // Step cursor
714 channel -> print( 'D' ); // Left.
715
716 #endif
717
718 }
719
720 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
721
722 setTerminalCharacterColor( acceleratorBufferPtr, REGULAR, WHITE );
723 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
724 channel -> print( acceleratorBuffer );
725
726 #else
727
729
730 #endif
731
732}
733
734void Shellminator::process( char new_char ) {
735
736 // Line endings:
737 // NetCat: \n
738 // Screen: \r \0
739 // PuTTY: \r \n
740
741 // General counter variable
742 uint32_t i;
743
744 /*
745 Serial.print( "New data : '" );
746 Serial.print(new_char);
747 Serial.print("' [0x");
748 Serial.print( new_char, HEX );
749 Serial.println("]");
750 */
751
752 if( new_char == '\0' ){
753 return;
754 }
755
756 if( new_char == '\n' ){
757 return;
758 }
759
760 // Check if the new character is backspace character.
761 // '\b' or 127 are both meaning that the backspace kes is pressed
762 if ( ( new_char == '\b' ) || ( new_char == 127 ) ) {
763
764 // If we press a backspace we have to reset cmd_buff_dim to default value
765 cmd_buff_dim = 1;
766
767 // We have to check the number of the characters in the buffer.
768 // If the buffer is empty we must not do anything!
769 if ( cursor > 0 ) {
770
771 // decrease the cmd buffer counter and the cursor position
772 cmd_buff_cntr--;
773 cursor--;
774
775 // if we are at the end of the command buffer
776 if ( cursor == cmd_buff_cntr ) {
777
778 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
779 if( inSearch ){
780
781 cmd_buff[ 0 ][ cursor + 1 ] = '\0'; // and from the cmd buffer
782 redrawLine();
783
784 }
785
786 else{
787 #endif
788
789 channel -> print("\b \b"); // just delete the last character from the terminal
790 cmd_buff[ 0 ][ cursor + 1 ] = '\0'; // and from the cmd buffer
791
792 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
793 }
794 #endif
795
796
797 }
798
799 else {
800
801 // if the cursor is somewhere in the middle of the cmd buffer
802 // rework the buffer and redraw the whole line
803 for( i = cursor; i < cmd_buff_cntr; i++ ) {
804
805 cmd_buff[ 0 ][ i ] = cmd_buff[ 0 ][ i + 1 ];
806
807 }
808
809 redrawLine();
810
811 }
812
813 // We have no more things to do, so return
814 return;
815
816 }
817
818 }
819
820 // If the enter key is pressed in the keyboard, the terminal application
821 // will send a '\r' character.
822 else if ( new_char == '\r' ) {
823
824 // If the enter key is pressed cmd_buff_dim has to be reset to the default value
825 cmd_buff_dim = 1;
826
827 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
828
829 if( inSearch ){
830
831 if( searchMatch > 0 ){
832
833 inSearch = false;
834 strncpy( cmd_buff[ 0 ], cmd_buff[ searchMatch ], SHELLMINATOR_BUFF_LEN + 1 );
835 cmd_buff_cntr = strlen( cmd_buff[ 0 ] );
836 redrawLine();
837
838 }
839
840 else{
841 cmd_buff_cntr = 0;
842 }
843
844 }
845
846 #endif
847
848 // Because a command is sent we have to close it. Basically we replace the arrived
849 // '\r' character with a '\0' string terminator character. Now we have our command
850 // in a C/C++ like standard string format.
851 cmd_buff[ 0 ][ cmd_buff_cntr ] = '\0';
852
853 // We send a line break to the terminal to put the next data in new line
854 channel -> print( '\r' );
855 channel -> print( '\n' );
856
857 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
858
859 inSearch = false;
860
861 #endif
862
863 // If the arrived data is not just a single enter we have to process the command.
864 if ( cmd_buff_cntr > 0 ) {
865
866 if( ( strcmp( cmd_buff[ 0 ], "help" ) == 0 ) || ( strcmp( cmd_buff[ 0 ], "?" ) == 0 ) ){
867
868 printHelp();
869
870 }
871
872 else if( strcmp( cmd_buff[ 0 ], "history" ) == 0 ){
873
874 printHistory();
875
876 }
877
878 // We haveto check that execution_fn is not NULL.
879 else if( execution_fn != NULL ){
880
881 // If it is a valid, then call it's function.
882 execution_fn( cmd_buff[ 0 ] );
883
884 }
885
886 #ifdef COMMANDER_API_VERSION
887
888 // If a Commander object is added, it can be used
889 // to execute the command without an execution_fn.
890 else if( commander != NULL ){
891
892 commander -> execute( cmd_buff[ 0 ], channel );
893
894 }
895
896 #endif
897
898 // If not, then just print it with Serial.
899 else{
900 channel -> print( (const char*)"cmd: " );
901 channel -> print( cmd_buff[ 0 ] );
902 }
903
904 // Send a new line after command execution,
905 // so we will not overwrite the last line of the
906 // command output with the banner
907 channel -> print( '\r' );
908 channel -> print( '\n' );
909
910 // After we processed the command we have to shift the history upwards.
911 // To protect the copy against buffer overflow we use strncpy
912 for ( i = ( SHELLMINATOR_BUFF_DIM - 1 ); i > 0; i-- ) {
913
914 strncpy( cmd_buff[ i ], cmd_buff[ i - 1 ], SHELLMINATOR_BUFF_LEN + 1 );
915
916 }
917
918 }
919
920 // After the command processing finished we print a new banner to the terminal.
921 // This means that the device is finished and waits the new command.
922 printBanner();
923
924 // To empty the incoming string we have to zero it's counter.
925 cmd_buff_cntr = 0;
926 cursor = 0;
927
928 // We have no more things to do, so return
929 return;
930
931 }
932
933 // This part handles the arrow detection. Arrows are composed as a VT100 command.
934 // These commands usually contains a pattern. This pattern usually starts with
935 // an Escape character, that is deciman 27 in ASCII table.
936 // The escape_state variable stores the state of the VT100 command interpreter
937 // state-machine.
938 else if ( new_char == 27 ) {
939
940 // If escape character recived we set escape_state variable to 1.
941 escape_state = 1;
942
943 // We have no more things to do, so return
944 return;
945 }
946
947 // If the escape_state variable is 1 that means we expect that the new character will be
948 // a '[' character.
949 else if ( escape_state == 1 ) {
950
951 // Check that the new character is '['
952 if ( new_char == '[' ) {
953
954 // If it is, we set escape_state variable to 2
955 escape_state = 2;
956
957 // We have no more things to do, so return
958 return;
959
960 }
961
962 else {
963
964 // If the new character is not '[', that means it is not a VT100 command so we have to stop
965 // the interpretation of the escape sequence.
966 escape_state = 0;
967
968 // We have no more things to do, so return
969 return;
970
971 }
972
973 }
974
975 // If the escape_state variable is 2 that means we expect that the new character will be
976 // an 'A', 'B', 'C' or 'D' character. These four characters are represent the four arrow keys.
977 // A -> Up
978 // B -> Down
979 // C -> Right
980 // D -> Left
981 else if ( escape_state == 2 ) {
982
983 // To chose between the four valid values the easyest way is a switch.
984 switch ( new_char ) {
985
986 // Up arrow pressed
987 case 'A':
988
989 // Because we have finished the ecape sequence interpretation we reset the state-machine.
990 escape_state = 0;
991
992 // Check if the arrow function is overriden.
993 if( upArrowOverrideFunc ){
994
995 upArrowOverrideFunc();
996 break;
997
998 }
999
1000 // We have to check that we can go upper in history
1001 if ( cmd_buff_dim < ( SHELLMINATOR_BUFF_DIM ) ) {
1002
1003 // If we can, we have to check that the previous command was not empty.
1004 if ( cmd_buff[ cmd_buff_dim ][0] == '\0' ) {
1005
1006 // If it was empty we can't do much with an empty command so we return.
1007 break;
1008
1009 }
1010
1011 // Now we have to copy the characters form the histoy to the 0th element in the buffer.
1012 // Remember the 0th element is always reserved for the new data. If we browse the history the
1013 // data in the history will overwrite the data in the 0th element so the historical data will be
1014 // the new data. We use strncpy to prevent overflow.
1015 strncpy( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim ], SHELLMINATOR_BUFF_LEN + 1 );
1016
1017 // We have to calculate the historical data length to pass it to the cmd_buff_cntr variable.
1018 // It is important to track the end of the loaded string.
1019 cmd_buff_cntr = strlen( cmd_buff[ 0 ] );
1020 cursor = cmd_buff_cntr;
1021
1022 // We print the loaded command to the terminal interface.
1023 //channel -> print( cmd_buff[ 0 ] );
1024
1025 redrawLine();
1026
1027 // We have to increment the cmd_buff_dim variable, to track the history position.
1028 // Greater number means older command!
1029 cmd_buff_dim++;
1030
1031 }
1032
1033 // We have finished so we can break from the switch.
1034 break;
1035
1036 // Down arrow pressed
1037 case 'B':
1038
1039 // Because we have finished the ecape sequence interpretation we reset the state-machine.
1040 escape_state = 0;
1041
1042 // Check if the arrow function is overriden.
1043 if( downArrowOverrideFunc ){
1044
1045 downArrowOverrideFunc();
1046 break;
1047
1048 }
1049
1050
1051 // We have to check that we can go lover in history, and we are not in the first previous command.
1052 if ( cmd_buff_dim > 2 ) {
1053
1054 // We have to decrement the cmd_buff_dim variable, to track the history position.
1055 // Lower number means newer command!
1056 cmd_buff_dim--;
1057
1058 // Now we have to copy the characters form the histoy to the 0th element in the buffer.
1059 // Remember the 0th element is always reserved for the new data. If we browse the history the
1060 // data in the history will overwrite the data in the 0th element so the historical data will be
1061 // the new data. We use strncpy to prevent overflow.
1062 strncpy( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim - 1 ], SHELLMINATOR_BUFF_LEN + 1 );
1063
1064 // We have to calculate the historical data length to pass it to the cmd_buff_cntr variable.
1065 // It is important to track the end of the loaded string.
1066 cmd_buff_cntr = strlen( cmd_buff[ 0 ] );
1067 cursor = cmd_buff_cntr;
1068
1069 // We print the loaded command to the terminal interface.
1070 //channel -> print( cmd_buff[ 0 ] );
1071 redrawLine();
1072
1073 }
1074
1075 // Check that if we are in the first previous command.
1076 else if ( cmd_buff_dim == 2 ) {
1077
1078 // To empty the incoming string we have to zero it's counter.
1079 cmd_buff_cntr = 0;
1080 cursor = 0;
1081
1082 // We have to reset the cmd_buff_dim variable to the default value.
1083 cmd_buff_dim = 1;
1084
1085 redrawLine();
1086
1087 }
1088
1089 // We have finished so we can break from the switch.
1090 break;
1091
1092 // Right arrow pressed
1093 // Currently not used
1094 case 'C':
1095
1096 // We just simply reset the state-machine.
1097 escape_state = 0;
1098
1099 // Check if the arrow function is overriden.
1100 if( rightArrowOverrideFunc ){
1101
1102 rightArrowOverrideFunc();
1103 break;
1104
1105 }
1106
1107 // Check if we can move to right.
1108 if( cursor < cmd_buff_cntr ){
1109
1110 channel -> write( 27 ); // ESC character( decimal 27 )
1111 channel -> print( '[' ); // VT100 Cursor command.
1112 channel -> print( '1' ); // 1 character movement.
1113 channel -> print( 'C' ); // Left.
1114
1115 // Increment the cursor variavble.
1116 cursor++;
1117
1118 }
1119
1120 // We have finished so we can break from the switch.
1121 break;
1122
1123 // Left arrow pressed
1124 // Currently not used
1125 case 'D':
1126
1127 // We just simply reset the state-machine.
1128 escape_state = 0;
1129
1130 // Check if the arrow function is overriden.
1131 if( leftArrowOverrideFunc ){
1132
1133 leftArrowOverrideFunc();
1134 break;
1135
1136 }
1137
1138 // Check if we can move to left.
1139 if( cursor > 0 ){
1140
1141 channel -> write( 27 ); // ESC character( decimal 27 )
1142 channel -> print( '[' ); // VT100 Cursor command.
1143 channel -> print( '1' ); // 1 character movement.
1144 channel -> print( 'D' ); // Left.
1145
1146 // Decrement the cursor variable.
1147 cursor--;
1148
1149 }
1150
1151 // We have finished so we can break from the switch.
1152 break;
1153
1154 // Home key pressed in xTerm.
1155 case 'H':
1156 escape_state = 0;
1157
1158 if( homeKeyFunc ){
1159
1160 homeKeyFunc();
1161 break;
1162
1163 }
1164
1165 // send the cursor to the begining of the buffer
1166 cursor = 0;
1167 redrawLine();
1168
1169 break;
1170
1171 // End key pressed in xTerm.
1172 case 'F':
1173 escape_state = 0;
1174
1175 if( endKeyFunc ){
1176
1177 endKeyFunc();
1178 break;
1179
1180 }
1181
1182 // send the cursor to the end of the buffer
1183 cursor = cmd_buff_cntr;
1184 redrawLine();
1185
1186 break;
1187
1188 // Check for Del key;
1189 case '3':
1190 escape_state = 3;
1191 break;
1192
1193 // Check for End key;
1194 case '4':
1195 escape_state = 4;
1196 break;
1197
1198 // Check for PgUp key;
1199 case '5':
1200 escape_state = 6;
1201 break;
1202
1203 // Check for PgUp key;
1204 case '6':
1205 escape_state = 7;
1206 break;
1207
1208 // Check for Home key;
1209 case '1':
1210 escape_state = 5;
1211 break;
1212
1213 // Any other cases means that it was probably a VT100 command but not supported.
1214 default:
1215
1216 // In this case we just simply reset the state-machine.
1217 escape_state = 0;
1218
1219 // We have finished so we can break from the switch.
1220 break;
1221
1222 }
1223
1224 // We have finished so we can return.
1225 return;
1226
1227 }
1228
1229 // Detect del key termination.
1230 else if ( escape_state == 3 ) {
1231
1232 if( new_char == '~' ){
1233
1234 // Del key detected.
1235 // If we press a delet key we have to reset cmd_buff_dim to default value
1236 cmd_buff_dim = 1;
1237
1238 // We have to check the number of the characters in the buffer.
1239 // If the buffer is full we must not do anything!
1240 if ( cursor != cmd_buff_cntr ) {
1241
1242 for( i = cursor; i < ( cmd_buff_cntr - 1 ); i++ ){
1243
1244 cmd_buff[ 0 ][ i ] = cmd_buff[ 0 ][ i + 1 ];
1245
1246 }
1247
1248 // If there is at least 1 character in the buffer we jus simply
1249 // decrement the cmd_buff_cntr. This will result that the new character
1250 // will be stored in the previous characters place in the buffer.
1251 cmd_buff_cntr--;
1252
1253 redrawLine();
1254
1255 }
1256
1257 }
1258
1259 escape_state = 0;
1260 return;
1261
1262 }
1263
1264 // Detect End key termination.
1265 else if ( escape_state == 4 ) {
1266
1267 escape_state = 0;
1268
1269 if( new_char == '~' ) {
1270
1271 if( endKeyFunc ){
1272
1273 endKeyFunc();
1274 return;
1275
1276 }
1277
1278 // send the cursor to the end of the buffer
1279 cursor = cmd_buff_cntr;
1280 redrawLine();
1281
1282 }
1283
1284 return;
1285
1286 }
1287
1288 // Detect Home key termination.
1289 else if ( escape_state == 5 ) {
1290
1291 escape_state = 0;
1292
1293 if( new_char == '~' ){
1294
1295 if( homeKeyFunc ){
1296
1297 homeKeyFunc();
1298 return;
1299
1300 }
1301
1302 // send the cursor to the begining of the buffer
1303 cursor = 0;
1304 redrawLine();
1305
1306 }
1307
1308 return;
1309
1310 }
1311
1312 else if( escape_state == 6 ){
1313
1314 escape_state = 0;
1315
1316 if( new_char == '~' ){
1317
1318 if( pageUpKeyFunc ){
1319
1320 pageUpKeyFunc();
1321 return;
1322
1323 }
1324
1325 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1326
1327 historySearchBackward();
1328
1329 #endif
1330
1331 }
1332
1333 return;
1334
1335 }
1336
1337 else if( escape_state == 7 ){
1338
1339 escape_state = 0;
1340
1341 if( new_char == '~' ){
1342
1343 if( pageDownKeyFunc ){
1344
1345 pageDownKeyFunc();
1346 return;
1347
1348 }
1349
1350 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1351
1352 historySearchForward();
1353
1354 #endif
1355
1356 }
1357
1358 return;
1359
1360 }
1361
1362 else if( new_char == 0x01 ){ // ctrl-a (beginning of the line)
1363 cursor = 0;
1364 redrawLine();
1365 return;
1366 }
1367
1368 else if( new_char == 0x05 ){ // ctrl-e (end of the line)
1369 cursor = cmd_buff_cntr;
1370 redrawLine();
1371 return;
1372 }
1373
1374 else if( new_char == 0x04 ){ // ctrl-d (logout)
1375
1376 if( logoutKeyFunc ){
1377
1378 logoutKeyFunc();
1379 return;
1380
1381 }
1382
1383 #ifdef SHELLMINATOR_USE_WIFI_CLIENT
1384
1386
1387 #endif
1388
1389 #ifdef SHELLMINATOR_ENABLE_WEBSOCKET_MODULE
1390
1391 websocketDisconnect();
1392
1393 #endif
1394
1395 return;
1396 }
1397
1398 else if( new_char == 0x12 ){ // ctrl-r (search)
1399
1400 if( searchKeyFunc ){
1401
1402 searchKeyFunc();
1403 return;
1404
1405 }
1406
1407 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1408
1409 inSearch = !inSearch;
1410 redrawLine();
1411
1412 #endif
1413
1414 return;
1415
1416 }
1417
1418 else if( new_char == 0x0C ){ // ctrl-l ( clear screen )
1419
1420 clear();
1421 redrawLine();
1422
1423 }
1424
1425 // Commander command search.
1426 else if( new_char == '\t' ){
1427
1428 // Auto complete section.
1429 #ifdef COMMANDER_API_VERSION
1430
1431 // Firstly, we have to set the cursor to the end of the input command.
1432 // If the algorythm fills the missing characters, they have to placed
1433 // at the end.
1434 cursor = cmd_buff_cntr;
1435
1436 // Pointer to a Commander API-tree element.
1437 Commander::API_t *commandAddress;
1438
1439 // The next auto filled character will be placed in this variable.
1440 char nextChar;
1441
1442 // This flag holds an auto complete conflict event.
1443 // Conflict event happens:
1444 // - after the first character of mismatch( restart, reboot will trigger conflict at the third character )
1445 // - if cmd_buff_cntr would overflow Commanders command tree.
1446 // - if we found the end of the last command.
1447 bool conflict = false;
1448
1449 // PROGMEM based tree is not supported for auto complete yet.
1450 if( commander -> memoryType != Commander::MEMORY_REGULAR ){
1451 return;
1452 }
1453
1454 // If there is no conflict event, we are trying
1455 // to fill as many characters as possible.
1456 while( !conflict ){
1457
1458 // Reset the counter to the first Commander API-tree element.
1459 i = 0;
1460
1461 // Get the address of the element indexed by i.
1462 // If the indexed elment does not exists, Commander
1463 // will return NULL.
1464 commandAddress = commander -> operator[]( (int)i );
1465
1466 // Set to default state.
1467 nextChar = '\0';
1468
1469 // Go through all elements in Commanders API-tree.
1470 while( commandAddress ){
1471
1472 // We have to check that the typed command is exists within an existing command.
1473 if( strncmp( (const char*)cmd_buff[ 0 ], commandAddress -> name, cmd_buff_cntr ) == 0 ){
1474
1475 // If it does, we have to check for conflict.
1476 if( ( nextChar == '\0' ) && ( cmd_buff_cntr < COMMANDER_MAX_COMMAND_SIZE ) && ( commandAddress -> name[ cmd_buff_cntr ] != '\0' ) ){
1477
1478 // If there is no conflict we can set the next character from the command that we found.
1479 nextChar = commandAddress -> name[ cmd_buff_cntr ];
1480
1481 }
1482
1483 else{
1484
1485 // We have to check that the next character in the command
1486 // tree is not the same as the value in nextChar.
1487 if( commandAddress -> name[ cmd_buff_cntr ] != nextChar ){
1488
1489 // Trigger conflict.
1490 conflict = true;
1491
1492 }
1493
1494 }
1495
1496 }
1497
1498 // Increment i to get the next command's index.
1499 i++;
1500
1501 // Get the address of the element indexed by i.
1502 commandAddress = commander -> operator[]( (int)i );
1503
1504 }
1505
1506 // If nextChar does not changed since start, that means
1507 // we did not found anything similar.
1508 if( nextChar == '\0' ){
1509
1510 // We have to trigger conflict to abort the process.
1511 conflict = true;
1512
1513 }
1514
1515 // If we does not had a conflict event, we have to process
1516 // the foind character as a regular character.
1517 if( !conflict ){
1518
1519 process( nextChar );
1520
1521 }
1522
1523 }
1524
1525
1526 #endif
1527
1528
1529 return;
1530
1531 }
1532
1533 // Abort key detection.
1534 else if( new_char == 0x03 ){
1535
1536 if( abortKeyFunc ){
1537
1538 abortKeyFunc();
1539
1540 }
1541
1542 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1543
1544 inSearch = false;
1545
1546 #endif
1547
1548 // If the abort key is pressed cmd_buff_dim has to be reset to the default value
1549 cmd_buff_dim = 1;
1550
1551 // We send a line break to the terminal to put the next data in new line
1552 channel -> print( '\r' );
1553 channel -> print( '\n' );
1554
1555 printBanner();
1556
1557 cursor = 0;
1558 cmd_buff_cntr = 0;
1559
1560 return;
1561
1562 }
1563
1564 // Any other cases means that the new character is just a simple character that was pressed on the keyboard.
1565 else {
1566
1567 // If the cursor is at the end of the command,
1568 // we simply store the new character.
1569 if( cursor == cmd_buff_cntr ){
1570
1571 cmd_buff[ 0 ][ cmd_buff_cntr ] = new_char;
1572
1573 }
1574
1575 // If the cursor is somewhere in the middle, we have to shift the
1576 // end of the command by one character end insert the new character
1577 // to the cursor position.
1578 else{
1579
1580 for( i = cmd_buff_cntr; i > cursor; i-- ){
1581
1582 cmd_buff[ 0 ][ i ] = cmd_buff[ 0 ][ i - 1 ];
1583
1584 }
1585
1586 // Add the new character after the cursor in the buffer
1587 cmd_buff[ 0 ][ cursor ] = new_char;
1588
1589 }
1590
1591 // In this case we have to reset the cmd_buff_dim variable to the default value.
1592 cmd_buff_dim = 1;
1593
1594 // If the cursor was at the end we have to print the
1595 // new character if the cmd_buff had free space at
1596 // the end.
1597 if ( cursor == cmd_buff_cntr ) {
1598
1599 if ( cmd_buff_cntr < SHELLMINATOR_BUFF_LEN ) {
1600
1601 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1602
1603 if( inSearch ){
1604
1605 // Increment counters.
1606 cmd_buff_cntr++;
1607 cursor++;
1608
1609 redrawLine();
1610
1611 // Increment counters.
1612 cmd_buff_cntr--;
1613 cursor--;
1614
1615 }
1616
1617 else{
1618 #endif
1619
1620 channel -> print(new_char);
1621
1622 #ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
1623
1624 }
1625
1626 #endif
1627
1628 }
1629
1630 }
1631
1632 // Increment counters.
1633 cmd_buff_cntr++;
1634 cursor++;
1635
1636 if ( cursor != cmd_buff_cntr ) {
1637
1638 // Redraw the command line.
1639 redrawLine();
1640
1641 }
1642
1643
1644 // Check if the counters are overloaded.
1645 // the buffer storage is SHELLMINATOR_BUFF_LEN + 2,
1646 // so it is safe to make the counters equeal to SHELLMINATOR_BUFF_LEN
1647 if( cmd_buff_cntr > SHELLMINATOR_BUFF_LEN ) {
1648
1649 cmd_buff_cntr = SHELLMINATOR_BUFF_LEN;
1650
1651 }
1652
1653 if( cursor > SHELLMINATOR_BUFF_LEN ) {
1654
1655 cursor = SHELLMINATOR_BUFF_LEN;
1656
1657 }
1658
1659
1660 // We have finished so we can return.
1661 return;
1662
1663 }
1664
1665}
1666
1667void Shellminator::overrideUpArrow( void( *func )( void ) ){
1668
1669 upArrowOverrideFunc = func;
1670
1671}
1672
1673void Shellminator::overrideDownArrow( void( *func )( void ) ){
1674
1675 downArrowOverrideFunc = func;
1676
1677}
1678
1679void Shellminator::overrideLeftArrow( void( *func )( void ) ){
1680
1681 leftArrowOverrideFunc = func;
1682
1683}
1684
1685void Shellminator::overrideRightArrow( void( *func )( void ) ){
1686
1687 rightArrowOverrideFunc = func;
1688
1689}
1690
1691void Shellminator::overrideAbortKey( void( *func )( void ) ){
1692
1693 abortKeyFunc = func;
1694
1695}
1696
1697void Shellminator::overridePageUpKey( void( *func )( void ) ){
1698
1699 pageUpKeyFunc = func;
1700
1701}
1702
1703void Shellminator::overridePageDownKey( void( *func )( void ) ){
1704
1705 pageDownKeyFunc = func;
1706
1707}
1708
1709void Shellminator::overrideHomeKey( void( *func )( void ) ){
1710
1711 homeKeyFunc = func;
1712
1713}
1714
1715void Shellminator::overrideEndKey( void( *func )( void ) ){
1716
1717 endKeyFunc = func;
1718
1719}
1720
1721void Shellminator::overrideLogoutKey( void( *func )( void ) ){
1722
1723 logoutKeyFunc = func;
1724
1725}
1726
1727void Shellminator::overrideSearchKey( void( *func )( void ) ){
1728
1729 logoutKeyFunc = func;
1730
1731}
1732
1734
1735 upArrowOverrideFunc = NULL;
1736
1737}
1738
1740
1741 downArrowOverrideFunc = NULL;
1742
1743}
1744
1746
1747 leftArrowOverrideFunc = NULL;
1748
1749}
1750
1752
1753 rightArrowOverrideFunc = NULL;
1754
1755}
1756
1758
1759 abortKeyFunc = NULL;
1760
1761}
1762
1764
1765 pageUpKeyFunc = NULL;
1766
1767}
1768
1770
1771 pageDownKeyFunc = NULL;
1772
1773}
1774
1776
1777 homeKeyFunc = NULL;
1778
1779}
1780
1782
1783 endKeyFunc = NULL;
1784
1785}
1786
1788
1789 logoutKeyFunc = NULL;
1790
1791}
1792
1794
1795 searchKeyFunc = NULL;
1796
1797}
1798
1799#ifdef SHELLMINATOR_USE_WIFI_CLIENT
1800
1802
1803 if( clientConnected && client.connected() ){
1804
1805 client.println( "Logout!" );
1806 client.stop();
1807
1808 }
1809
1810}
1811
1812#endif
1813
1815
1816 // This variable will hold the character that was read from the channel buffer.
1817 char c;
1818
1819 #ifdef SHELLMINATOR_USE_WIFI_CLIENT
1820
1821 if( server ){
1822
1823 if( server -> hasClient() ){
1824
1825 // If we are alredy connected, we have to reject the new connection.
1826 if( CLIENT_STATE ){
1827
1828 // Connection reject event!
1829 server -> available().stop();
1830
1831 }
1832
1833 else{
1834
1835 // New connection event!
1836 client = server -> available();
1837 client.setNoDelay(false);
1838 client.setTimeout( 1000 );
1839 clientConnected = true;
1840
1841 client.write( TELNET_IAC_DONT_LINEMODE, 3 );
1842 client.write( TELNET_IAC_WILL_ECHO, 3 );
1843 client.write( TELNET_IAC_DONT_ECHO, 3 );
1844 client.write( TELNET_IAC_WILL_SUPRESS_GO_AHEAD, 3 );
1845 client.write( TELNET_IAC_DO_SUPRESS_GO_AHEAD, 3 );
1846
1847 // Initialise the wifiChannel as communication channel
1848 // to draw the logo and the banner.
1849 channel = &client;
1850
1851 // Set the terminal color and style to the defined settings for the logo
1853
1854 drawLogo();
1855
1856 printBanner();
1857
1858 }
1859
1860 }
1861
1862 // Check for disconnection
1863 if( clientConnected && !CLIENT_STATE ){
1864
1865 // Client distonnect event!
1866
1867 // The TX and RX buffers has to be cleared.
1868 // I did not noticed it on the ESP32 but it was
1869 // visible with ESP8266 at new connection.
1870 client.flush();
1871 delay( 100 );
1872 while( client.available() ){
1873 client.read();
1874 }
1875
1876 client.stop();
1877 clientConnected = false;
1878
1879 }
1880
1881 // If connected, we have to process the Telnet commands.
1882 if( clientConnected && CLIENT_STATE ){
1883
1884 // Check for availabla data.
1885 if( client.available() ){
1886
1887 // Telnet command state machine.
1888 switch( telnetNegotiationState ){
1889
1890 // In case 0 we have to check the next element in the buffer.
1891 // If it 0xFF, that means, we have an ongoing Telnet command,
1892 // so we have to parse it in the next state( state 1 ). Any other
1893 // situations we select the internal WiFiClient as channel to
1894 // let Shellminator process the data.
1895 case 0:
1896
1897 if( client.peek() == 0xFF ){
1898
1899 // Read the data to remove it from the buffer.
1900 client.read();
1901
1902 // Switch to the next state.
1903 telnetNegotiationState = 1;
1904
1905 // Set the communication channel to the default one,
1906 // to not do anything in the next section.
1907 channel = &defaultChannel;
1908
1909 }
1910
1911 else{
1912
1913 // Initialise the client as communication channel.
1914 channel = &client;
1915
1916 }
1917
1918 break;
1919
1920 case 1:
1921
1922 // This byte is the Telnet command.
1923 // Right now we don't do anything
1924 // with it, but we have to read it,
1925 // to remove it from the buffer.
1926 client.read();
1927
1928 // Switch to the next state.
1929 telnetNegotiationState = 2;
1930
1931 // Set the communication channel to the default one,
1932 // to not do anything in the next section.
1933 channel = &defaultChannel;
1934
1935 break;
1936
1937 case 2:
1938
1939 // This byte is the Telnet option.
1940 // Right now we don't do anything
1941 // with it, but we have to read it,
1942 // to remove it from the buffer.
1943 client.read();
1944
1945 // Switch to the default state( state 0 ).
1946 telnetNegotiationState = 0;
1947 break;
1948
1949 // Set the communication channel to the default one,
1950 // to not do anything in the next section.
1951 channel = &defaultChannel;
1952
1953 default:
1954 // Something went wrong, we should not be here.
1955 // Switch to the default state( state 0 ).
1956 telnetNegotiationState = 0;
1957 break;
1958
1959 }
1960
1961 }
1962
1963 }
1964
1965 // Else set the default channel. The default channel's
1966 // available function will return 0, so the next part of
1967 // the update function won't do anything( as should ).
1968 else{
1969
1970 channel = &defaultChannel;
1971
1972 }
1973
1974 }
1975
1976 #endif
1977
1978 // We have to check the channel buffer. If it is not empty we can read as many characters as possible.
1979 while ( channel -> available() ) {
1980
1981 // Read one character from channel Buffer.
1982 c = (char)channel -> read();
1983
1984 // Process the new character.
1985 process( c );
1986
1987 #ifdef COMMANDER_API_VERSION
1988 commandCheckTimerStart = millis();
1989 commandChecked = false;
1990 #endif
1991
1992 }
1993
1994 #ifdef COMMANDER_API_VERSION
1995
1996 // Command highlight section.
1997 Commander::API_t *commandAddress;
1998
1999 // If Commander is available and we did not checked the
2000 // typed command, we are trying to find and highlight it.
2001 if( commander && !commandChecked ){
2002 bool previousCommandFound = commandFound; // hold the previos flag
2003
2004 // We have to wait 100ms after the last keypress.
2005 if( ( millis() - commandCheckTimerStart ) > 100 ){
2006
2007 // Generic counter variable.
2008 uint32_t i = 0;
2009
2010 // Commander expects a null terminated string, so we
2011 // have to terminate the string at the end, or at
2012 // space character. But after the search we have to
2013 // store bactk this character to it's original state.
2014 char charCopy = 0; // initialize the variable
2015
2016 // Find the end of the input command, or the first space
2017 // character in it, store it's value to charCopy, and
2018 // replace it with null character.
2019 for( i = 0; i <= cmd_buff_cntr; i++ ){
2020
2021 if( ( cmd_buff[ 0 ][ i ] == ' ' ) || ( i == cmd_buff_cntr ) ){
2022
2023 charCopy = cmd_buff[ 0 ][ i ];
2024 cmd_buff[ 0 ][ i ] = '\0';
2025 break;
2026
2027 }
2028
2029 }
2030
2031 // Try to find the command in Commander's API-tree.
2032 commandAddress = commander -> operator[]( cmd_buff[0] );
2033
2034 // If Commander responds with a non-null pointer, it means
2035 // that we have a mach.
2036 if( commandAddress ){
2037 commandFound = true;
2038 } else {
2039 commandFound = false;
2040 }
2041
2042 // Restore the original state.
2043 cmd_buff[ 0 ][ i ] = charCopy;
2044
2045 // Set the flag.
2046 commandChecked = true;
2047
2048 // if the commandFound flag has changed, redraw the line
2049 // to get the colors right
2050 if (previousCommandFound != commandFound) {
2051 redrawLine();
2052 }
2053
2054 }
2055
2056 }
2057
2058 #endif
2059
2060}
2061
2062void Shellminator::setTerminalCharacterColor( uint8_t style, uint8_t color ) {
2063
2064 if( !enableFormatting ){
2065
2066 return;
2067
2068 }
2069
2070 // The reference what I used can be found here: https://www.nayab.xyz/linux/escapecodes.html
2071 channel -> write( 27 );
2072 channel -> print( '[' );
2073 channel -> print( style );
2074 channel -> print( ';' );
2075 channel -> print( color );
2076 channel -> print( 'm' );
2077
2078}
2079
2080void Shellminator::setTerminalCharacterColor( char* buff, uint8_t style, uint8_t color ){
2081
2082 if( !enableFormatting ){
2083
2084 return;
2085
2086 }
2087
2088 if( buff == NULL ){
2089
2090 return;
2091
2092 }
2093
2094 sprintf( buff, "\033[%d;%dm", style, color );
2095
2096}
2097
2098void Shellminator::setTerminalCharacterColor( Stream *stream_p, uint8_t style, uint8_t color ){
2099
2100 // The reference what I used can be found here: https://www.nayab.xyz/linux/escapecodes.html
2101 stream_p -> write( 27 );
2102 stream_p -> print( '[' );
2103 stream_p -> print( style );
2104 stream_p -> print( ';' );
2105 stream_p -> print( color );
2106 stream_p -> print( 'm' );
2107
2108}
2109
2111
2112 if( logo ){
2113
2114 // Set the terminal color and style to the defined settings for the logo
2116
2117 // Draws the startup logo to the terminal interface.
2118 channel -> print( logo );
2119
2120 // Set the terminal style to normal.
2122
2123 }
2124
2125 #ifdef __AVR__
2126
2127 else if( progmemLogo ){
2128
2129 // Set the terminal color and style to the defined settings for the logo
2131
2132 // Draws the startup logo to the terminal interface.
2133 channel -> print( progmemLogo );
2134
2135 // Set the terminal style to normal.
2137
2138 }
2139
2140 #endif
2141
2142}
2143
2145
2146 if( !mute ){
2147
2148 // Bell character.
2149 channel -> print( '\a' );
2150
2151 }
2152
2153}
2154
2155#ifdef SHELLMINATOR_ENABLE_SEARCH_MODULE
2156
2157void Shellminator::historySearchBackward(){
2158
2159 // Store a copy of the cmd_buff_dim variable.
2160 // It is necessary, because it can be only changed
2161 // when we find a command. Other case it has to be intact.
2162 uint32_t cmd_buff_dim_save;
2163
2164 // Create a copy of cmd_buff_dim.
2165 cmd_buff_dim_save = cmd_buff_dim;
2166
2167 // We search upward the history until the end of the history memory, or match.
2168 while( cmd_buff_dim_save < ( SHELLMINATOR_BUFF_DIM ) ){
2169
2170 // Check for match.
2171 if( strncmp( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim_save ], cursor ) == 0 ){
2172
2173 // If we found a command we have to save the actual position to cmd_buff_dim.
2174 cmd_buff_dim = cmd_buff_dim_save;
2175
2176 // Copy the found data to the 0-th element.
2177 strncpy( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim ], SHELLMINATOR_BUFF_LEN + 1 );
2178
2179 // We have to calculate the historical data length to pass it to the cmd_buff_cntr variable.
2180 // It is important to track the end of the loaded string.
2181 // In this case the cursor does not move!
2182 cmd_buff_cntr = strlen( cmd_buff[ 0 ] );
2183
2184 // We print the loaded command to the terminal interface.
2185 redrawLine();
2186
2187 // Increment the buffer before return.
2188 // If we don't do this we can not search further.
2189 cmd_buff_dim++;
2190
2191 // Return because we have found the command.
2192 return;
2193
2194 }
2195
2196 // Incrememnt the position to test another command in history.
2197 cmd_buff_dim_save++;
2198
2199 }
2200
2201 // We did not found a command in the history.
2202 // Generate a beep to notify that.
2203 beep();
2204
2205}
2206
2207void Shellminator::historySearchForward(){
2208
2209 uint32_t cmd_buff_dim_save;
2210
2211 cmd_buff_dim_save = cmd_buff_dim;
2212
2213 while( cmd_buff_dim_save > 2 ){
2214
2215 cmd_buff_dim_save--;
2216
2217 if( strncmp( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim_save - 1 ], cursor ) == 0 ){
2218
2219 cmd_buff_dim = cmd_buff_dim_save;
2220
2221 strncpy( cmd_buff[ 0 ], cmd_buff[ cmd_buff_dim - 1 ], SHELLMINATOR_BUFF_LEN + 1 );
2222
2223 cmd_buff_cntr = strlen( cmd_buff[ 0 ] );
2224
2225 // We print the loaded command to the terminal interface.
2226 redrawLine();
2227
2228 return;
2229
2230 }
2231
2232 }
2233
2234 beep();
2235
2236}
2237
2238void Shellminator::redrawHistorySearch(){
2239
2240 uint32_t i;
2241 uint32_t j;
2242 bool highlighted = false;
2243 int32_t searchResult;
2244
2245 searchMatch = -1;
2246
2247 if( cmd_buff_cntr > SHELLMINATOR_BUFF_LEN ){
2248
2249 cmd_buff_cntr = SHELLMINATOR_BUFF_LEN;
2250
2251 }
2252
2253 // Terminate the command at the cmd_buff_cntr
2254 // to not print out the previous command's data.
2255 cmd_buff[ 0 ][ cmd_buff_cntr ] = '\0';
2256
2257 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2258
2259 acceleratorBufferPtr = acceleratorBuffer;
2260 // acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[%dC\033[0K", lastBannerSize );
2261 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[0;37m(reverse-i-search)'\033[1;33m%s\033[0;37m': \033[0K", cmd_buff[ 0 ] );
2262
2263 #else
2264
2265 channel -> print( '\r' );
2267 channel -> print( "(reverse-i-search)'" ); // 19 character
2269 channel -> print( cmd_buff[ 0 ] );
2271 channel -> print( "': \033[0K" );
2272
2273 #endif
2274
2275 if( cmd_buff_cntr == 0 ){
2276
2277 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2278
2279 channel -> print(acceleratorBuffer);
2280
2281 #endif
2282
2283 return;
2284
2285 }
2286
2287 for( i = 1; i < SHELLMINATOR_BUFF_DIM; i++ ){
2288
2289 searchResult = substring( cmd_buff[ 0 ], cmd_buff[ i ] );
2290 if( searchResult >= 0 ){
2291
2292 /*
2293 Serial.print( "found: " );
2294 Serial.print( i );
2295 Serial.print( ' ' );
2296 Serial.print( cmd_buff[ i ] );
2297 Serial.print( ' ' );
2298 Serial.println( searchResult );
2299 */
2300
2301 searchMatch = i;
2302
2303 for( j = 0; j < strlen( cmd_buff[ i ] ); j++ ){
2304
2305 if( !highlighted && ( j == searchResult ) ){
2306
2307 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2308
2309 setTerminalCharacterColor( acceleratorBufferPtr, BOLD, YELLOW );
2310 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
2311
2312 #else
2313
2315
2316 #endif
2317
2318 highlighted = true;
2319
2320 }
2321
2322 if( highlighted && ( j == ( searchResult + strlen( cmd_buff[ 0 ] ) ) ) ){
2323
2324 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2325
2326 setTerminalCharacterColor( acceleratorBufferPtr, REGULAR, WHITE );
2327 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
2328
2329 #else
2330
2332
2333 #endif
2334
2335 highlighted = false;
2336
2337 }
2338
2339 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2340
2341 *acceleratorBufferPtr = cmd_buff[ i ][ j ];
2342 acceleratorBufferPtr++;
2343
2344 #else
2345
2346 channel -> print( cmd_buff[ i ][ j ] );
2347
2348 #endif
2349
2350 }
2351
2352 if( highlighted ){
2353
2354 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2355
2356 setTerminalCharacterColor( acceleratorBufferPtr, REGULAR, WHITE );
2357 acceleratorBufferPtr = acceleratorBuffer + strlen( acceleratorBuffer );
2358
2359 #else
2360
2362
2363 #endif
2364
2365 }
2366
2367 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2368
2369 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[%dC", uint8_t( 19 + cursor ) );
2370 channel -> print( acceleratorBuffer );
2371
2372 #else
2373
2374 channel -> print( '\r' );
2375
2376 channel -> write( 27 );
2377 channel -> print( '[' );
2378 channel -> print( uint8_t( 19 + cursor ) );
2379 channel -> print( 'C' );
2380
2381 #endif
2382
2383 return;
2384
2385 }
2386
2387 }
2388
2389 #ifdef SHELLMINATOR_ENABLE_HIGH_MEMORY_USAGE
2390
2391 acceleratorBufferPtr += sprintf( acceleratorBufferPtr, "\r\033[%dC", uint8_t( 19 + cursor ) );
2392 channel -> print( acceleratorBuffer );
2393
2394 #else
2395
2396 channel -> print( '\r' );
2397
2398 channel -> write( 27 );
2399 channel -> print( '[' );
2400 channel -> print( uint8_t( 19 + cursor ) );
2401 channel -> print( 'C' );
2402
2403 #endif
2404
2405}
2406
2407int Shellminator::substring( char* str1, char* str2 ){
2408
2409 // https://www.geeksforgeeks.org/check-string-substring-another/
2410
2411 int i;
2412 int j;
2413
2414 int m = strlen( str1 );
2415 int n = strlen( str2 );
2416
2417 for( i = 0; i <= ( n - m ); i++ ){
2418
2419 for( j = 0; j < m; j++ ){
2420
2421 if( str2[ i + j ] != str1[ j ] ){
2422 break;
2423 }
2424
2425 }
2426
2427 if( j == m ){
2428
2429 return i;
2430
2431 }
2432
2433 }
2434
2435 return -1;
2436
2437}
2438
2439#endif
2440
2441#ifdef COMMANDER_API_VERSION
2442
2443void Shellminator::attachCommander( Commander* commander_p ){
2444
2445 commander = commander_p;
2446
2447}
2448
2449#endif
2450
2451#ifdef SHELLMINATOR_ENABLE_PASSWORD_MODULE
2452
2453void Shellminator::enablePasswordProtection( uint8_t* passwordHashAddress_p ){
2454
2455 passwordHashAddress = passwordHashAddress_p;
2456
2457}
2458
2459void Shellminator::enablePasswordProtection( const uint8_t* passwordHashAddress_p ){
2460
2461 passwordHashAddress = (uint8_t*)passwordHashAddress_p;
2462
2463}
2464
2465void Shellminator::enablePasswordProtection( char* passwordHashAddress_p ){
2466
2467 passwordHashAddress = (uint8_t*)passwordHashAddress_p;
2468
2469}
2470
2471void Shellminator::enablePasswordProtection( const char* passwordHashAddress_p ){
2472
2473 passwordHashAddress = (uint8_t*)passwordHashAddress_p;
2474
2475}
2476
2477void Shellminator::disablePasswordProtection(){
2478
2479 passwordHashAddress = NULL;
2480
2481}
2482
2483bool Shellminator::checkPassword( uint8_t* pwStr ){
2484
2485 terminal_sha256_init( &passwordHashCtx );
2486 terminal_sha256_update( &passwordHashCtx, pwStr, strlen( (const char*)pwStr ) );
2487 terminal_sha256_final( &passwordHashCtx, passwordHashBuffer );
2488
2489 return memcmp( passwordHashAddress, passwordHashBuffer, SHA256_BLOCK_SIZE );
2490
2491}
2492
2493bool Shellminator::checkPassword( const uint8_t* pwStr ){
2494
2495 checkPassword( (uint8_t*)pwStr );
2496
2497}
2498
2499bool Shellminator::checkPassword( char* pwStr ){
2500
2501 checkPassword( (uint8_t*)pwStr );
2502
2503}
2504
2505bool Shellminator::checkPassword( const char* pwStr ){
2506
2507 checkPassword( (uint8_t*)pwStr );
2508
2509}
2510
2511#endif
2512
2513
2514//----- QR-code generator part -----//
2515#ifdef SHELLMINATOR_ENABLE_QR_SUPPORT
2516
2517void Shellminator::generateQRText( char* text, enum qrcodegen_Ecc ecc ){
2518
2519 // The result of the QR-code generation will be stored in this variable.
2520 bool result;
2521
2522 // The actual size of the QR-code will be stored in this variable.
2523 uint32_t qr_size;
2524
2525 // This variable helps to track the x direction while drawing the QR code.
2526 uint32_t x;
2527
2528 // This variable helps to track the y direction while drawing the QR code.
2529 uint32_t y;
2530
2531 // This variable will store the upper pixel while drawing.
2532 bool upper_pixel;
2533
2534 // This variable will store the lower pixel while drawing.
2535 bool lower_pixel;
2536
2537 // Generate the QR-code with default settings, and store the result to
2538 // the result variable.
2539 result = qrcodegen_encodeText( text,
2540 qr_tempBuff,
2541 qr_data,
2542 ecc,
2546 true
2547 );
2548
2549 // We have to check the result variable. If it is true,
2550 // the QR-code generation went well.
2551 if( !result ){
2552
2553 return;
2554
2555 }
2556
2557 // Determinate the size of the QR-code.
2558 qr_size = qrcodegen_getSize( qr_data );
2559
2560 // The unicode character table does not have a rectangular square,
2561 // but it has a half rectangular square on, top, bottom, and a full bar.
2562 // Check these links to make it more clear:
2563 // -Full bar: https://www.fileformat.info/info/unicode/char/2588/index.htm
2564 // -Upper square: https://www.fileformat.info/info/unicode/char/2580/index.htm
2565 // -lower square: https://www.fileformat.info/info/unicode/char/2584/index.htm
2566 // To draw a QR-code with a terminal emulator the easiest way to combine these
2567 // unicode characters. Because it is two 'pixels' high, we have to step the y
2568 // variable by two lines.
2569 for( y = 0; y < ( qr_size / 2 ); y++ ){
2570
2571 // Print a new line at the begining of the horizontal drawing.
2572 channel -> print( "\r\n" );
2573
2574 // Draw all horizontal 'pixels'.
2575 for( x = 0; x < qr_size; x++ ){
2576
2577 // Determinate the upper pixel value.
2578 upper_pixel = qrcodegen_getModule( qr_data, x, ( y * 2 ) );
2579
2580 // Determinate the lower pixel value.
2581 lower_pixel = qrcodegen_getModule( qr_data, x, ( ( y * 2 ) + 1 ) );
2582
2583 // Draw the right pattern accordingly.
2584
2585 // Both pixels are black.
2586 if( upper_pixel && lower_pixel ){
2587
2588 channel -> print( "\u2588" );
2589 continue;
2590
2591 }
2592
2593 // Upper pixel is black.
2594 if( upper_pixel && ( !lower_pixel ) ){
2595
2596 channel -> print( "\u2580" );
2597 continue;
2598
2599 }
2600
2601 // Lower pixel is black.
2602 if( ( !upper_pixel ) && lower_pixel ){
2603
2604 channel -> print( "\u2584" );
2605 continue;
2606
2607 }
2608
2609 // If we get here we have to draw an empty bar.
2610 // The space character will do the job.
2611 channel -> print( " " );
2612
2613 }
2614
2615 }
2616
2617 // If the QR code size is not even, we have to draw
2618 // the last line as well.
2619 if( ( qr_size % 2 ) != 0 ){
2620
2621 // Print a new line at the begining of the horizontal drawing.
2622 channel -> print( "\r\n" );
2623
2624 // Draw all horizontal 'pixels'.
2625 for( x = 0; x < qr_size; x++ ){
2626
2627
2628 // Determinate the pixel value. We store it to upper_pixel variable.
2629 upper_pixel = qrcodegen_getModule( qr_data, x, ( qr_size - 1 ) );
2630
2631 // Check if we have to draw.
2632 if( upper_pixel ){
2633
2634 channel -> print( "\u2580" );
2635 continue;
2636
2637 }
2638
2639 // If we get here we have to draw an empty bar.
2640 // The space character will do the job.
2641 channel -> print( " " );
2642
2643 }
2644
2645 }
2646
2647 // Finally create a new line.
2648 channel -> print( "\r\n" );
2649
2650}
2651
2652void Shellminator::generateQRText( const char* text, enum qrcodegen_Ecc ecc ){
2653
2654 generateQRText( (char*)text, ecc );
2655
2656}
2657
2658void Shellminator::generateQRText( const char* text ){
2659
2660 generateQRText( (char*)text, qrcodegen_Ecc_MEDIUM );
2661
2662}
2663
2665
2667
2668}
2669
2670#endif
#define SHELLMINATOR_BANNER_PATH_LEN
#define SHELLMINATOR_BUFF_LEN
#define SHELLMINATOR_BANNER_LEN
#define SHELLMINATOR_LOGO_COLOR
Style of the startup logo.
#define SHELLMINATOR_BUFF_DIM
#define SHELLMINATOR_LOGO_FONT_STYLE
Definition of the maximum length of each command.
#define CLIENT_STATE
const char Shellminator::helpText[] PROGMEM
#define SHELLMINATOR_VERSION
+---— Custom configuration ---—+ | | | This is where you have to config | | your defines!...
void begin(char *banner_p)
Shellminator initialization function.
void overrideHomeKey(void(*func)(void))
Override Home key behaviour.
void generateQRText(char *text)
This function generates a QR-code from text.
void overrideLogoutKey(void(*func)(void))
Override Logout key behaviour.
void clear()
Clear screen.
void freeLeftArrow()
Reset left arrow key functionality to default.
void overrideUpArrow(void(*func)(void))
Override up arrow key behaviour.
void printHistory()
void update()
Update function.
void addExecFunc(void(*execution_fn_p)(char *))
Execution function adder function.
bool enableFormatting
This flag enables or disables character formatting.
void overrideRightArrow(void(*func)(void))
Override right arrow key behaviour.
void freeAbortKey()
Reset abort key functionality to default.
void freePageUpKey()
Reset Page-Up key functionality to default.
void clientDisconnect()
Disconnect WiFiClient telnet client.
void freeLogoutKey()
Reset Logout key functionality to default.
void freeEndKey()
Reset End key functionality to default.
void sendBackspace()
Sends a backspace.
void overrideEndKey(void(*func)(void))
Override End key behaviour.
void beginServer()
void printBanner()
This function prints the banner text.
Shellminator(WiFiServer *server_p)
void freeUpArrow()
Reset up arrow key functionality to default.
void overrideDownArrow(void(*func)(void))
Override down arrow key behaviour.
void freeHomeKey()
Reset Home key functionality to default.
void beep()
Generate a beep sound on the terminal device.
void overridePageDownKey(void(*func)(void))
Override Page-Down key behaviour.
void setBannerPathText(char *bannerPath_p)
void overridePageUpKey(void(*func)(void))
Override Page-Up key behaviour.
void overrideSearchKey(void(*func)(void))
Override Search key behaviour.
void overrideAbortKey(void(*func)(void))
Override abort key behaviour.
void attachLogo(char *logo_p)
This function attaches a logo to the terminal.
void overrideLeftArrow(void(*func)(void))
Override left arrow key behaviour.
void setTerminalCharacterColor(uint8_t style, uint8_t color)
Bring some color into your code.
void freeRightArrow()
Reset right arrow key functionality to default.
void drawLogo()
Draws the startup logo.
void setBannerText(char *banner_p)
This function sets the banner text.
void setClientTimeout(uint16_t clientTimeout_p)
void freeSearchKey()
Reset Search key functionality to default.
void freeDownArrow()
Reset down arrow key functionality to default.
static const char * version
String that holds the version information.
void freePageDownKey()
Reset Page-Down key functionality to default.
bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl)
Definition: qrcodegen.c:129
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y)
Definition: qrcodegen.c:762
int qrcodegen_getSize(const uint8_t qrcode[])
Definition: qrcodegen.c:752
#define qrcodegen_VERSION_MAX
Definition: qrcodegen.h:133
qrcodegen_Ecc
Definition: qrcodegen.h:57
@ qrcodegen_Ecc_MEDIUM
Definition: qrcodegen.h:61
#define qrcodegen_VERSION_MIN
Definition: qrcodegen.h:132
@ qrcodegen_Mask_AUTO
Definition: qrcodegen.h:73