Merge pull request #2113 from thinkyhead/command_sanitizer

Command sanitizer
This commit is contained in:
Scott Lahteine 2015-05-21 16:21:58 -07:00
commit 4d11b29959

View file

@ -141,7 +141,7 @@
* M112 - Emergency stop * M112 - Emergency stop
* M114 - Output current position to serial port * M114 - Output current position to serial port
* M115 - Capabilities string * M115 - Capabilities string
* M117 - display message * M117 - Display a message on the controller screen
* M119 - Output Endstop status to serial port * M119 - Output Endstop status to serial port
* M120 - Enable endstop detection * M120 - Enable endstop detection
* M121 - Disable endstop detection * M121 - Disable endstop detection
@ -236,6 +236,7 @@ bool axis_known_position[3] = { false };
static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0; static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0;
static char *current_command, *current_command_args;
static int cmd_queue_index_r = 0; static int cmd_queue_index_r = 0;
static int cmd_queue_index_w = 0; static int cmd_queue_index_w = 0;
static int commands_in_queue = 0; static int commands_in_queue = 0;
@ -265,7 +266,7 @@ static bool relative_mode = false; //Determines Absolute or Relative Coordinate
static char serial_char; static char serial_char;
static int serial_count = 0; static int serial_count = 0;
static boolean comment_mode = false; static boolean comment_mode = false;
static char *strchr_pointer; ///< A pointer to find chars in the command string (X, Y, Z, E, etc.) static char *seen_pointer; ///< A pointer to find chars in the command string (X, Y, Z, E, etc.)
const char* queued_commands_P= NULL; /* pointer to the current line in the active sequence of commands, or NULL when none */ const char* queued_commands_P= NULL; /* pointer to the current line in the active sequence of commands, or NULL when none */
const int sensitive_pins[] = SENSITIVE_PINS; ///< Sensitive pin list for M42 const int sensitive_pins[] = SENSITIVE_PINS; ///< Sensitive pin list for M42
// Inactivity shutdown // Inactivity shutdown
@ -785,21 +786,20 @@ void get_command() {
fromsd[cmd_queue_index_w] = false; fromsd[cmd_queue_index_w] = false;
#endif #endif
if (strchr(command, 'N') != NULL) { char *npos = strchr(command, 'N');
strchr_pointer = strchr(command, 'N'); char *apos = strchr(command, '*');
gcode_N = (strtol(strchr_pointer + 1, NULL, 10)); if (npos) {
gcode_N = strtol(npos + 1, NULL, 10);
if (gcode_N != gcode_LastN + 1 && strstr_P(command, PSTR("M110")) == NULL) { if (gcode_N != gcode_LastN + 1 && strstr_P(command, PSTR("M110")) == NULL) {
gcode_line_error(PSTR(MSG_ERR_LINE_NO)); gcode_line_error(PSTR(MSG_ERR_LINE_NO));
return; return;
} }
if (strchr(command, '*') != NULL) { if (apos) {
byte checksum = 0; byte checksum = 0, count = 0;
byte count = 0;
while (command[count] != '*') checksum ^= command[count++]; while (command[count] != '*') checksum ^= command[count++];
strchr_pointer = strchr(command, '*');
if (strtol(strchr_pointer + 1, NULL, 10) != checksum) { if (strtol(apos + 1, NULL, 10) != checksum) {
gcode_line_error(PSTR(MSG_ERR_CHECKSUM_MISMATCH)); gcode_line_error(PSTR(MSG_ERR_CHECKSUM_MISMATCH));
return; return;
} }
@ -813,27 +813,25 @@ void get_command() {
gcode_LastN = gcode_N; gcode_LastN = gcode_N;
// if no errors, continue parsing // if no errors, continue parsing
} }
else { // if we don't receive 'N' but still see '*' else if (apos) { // No '*' without 'N'
if ((strchr(command, '*') != NULL)) {
gcode_line_error(PSTR(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM), false); gcode_line_error(PSTR(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM), false);
return; return;
} }
}
if (strchr(command, 'G') != NULL) { // Movement commands alert when stopped
strchr_pointer = strchr(command, 'G'); if (IsStopped()) {
switch (strtol(strchr_pointer + 1, NULL, 10)) { char *gpos = strchr(command, 'G');
if (gpos) {
int codenum = strtol(gpos + 1, NULL, 10);
switch (codenum) {
case 0: case 0:
case 1: case 1:
case 2: case 2:
case 3: case 3:
if (IsStopped()) {
SERIAL_ERRORLNPGM(MSG_ERR_STOPPED); SERIAL_ERRORLNPGM(MSG_ERR_STOPPED);
LCD_MESSAGEPGM(MSG_STOPPED); LCD_MESSAGEPGM(MSG_STOPPED);
break;
} }
break;
default:
break;
} }
} }
@ -916,32 +914,32 @@ void get_command() {
bool code_has_value() { bool code_has_value() {
int i = 1; int i = 1;
char c = strchr_pointer[i]; char c = seen_pointer[i];
if (c == '-' || c == '+') c = strchr_pointer[++i]; if (c == '-' || c == '+') c = seen_pointer[++i];
if (c == '.') c = strchr_pointer[++i]; if (c == '.') c = seen_pointer[++i];
return (c >= '0' && c <= '9'); return (c >= '0' && c <= '9');
} }
float code_value() { float code_value() {
float ret; float ret;
char *e = strchr(strchr_pointer, 'E'); char *e = strchr(seen_pointer, 'E');
if (e) { if (e) {
*e = 0; *e = 0;
ret = strtod(strchr_pointer+1, NULL); ret = strtod(seen_pointer+1, NULL);
*e = 'E'; *e = 'E';
} }
else else
ret = strtod(strchr_pointer+1, NULL); ret = strtod(seen_pointer+1, NULL);
return ret; return ret;
} }
long code_value_long() { return strtol(strchr_pointer + 1, NULL, 10); } long code_value_long() { return strtol(seen_pointer + 1, NULL, 10); }
int16_t code_value_short() { return (int16_t)strtol(strchr_pointer + 1, NULL, 10); } int16_t code_value_short() { return (int16_t)strtol(seen_pointer + 1, NULL, 10); }
bool code_seen(char code) { bool code_seen(char code) {
strchr_pointer = strchr(command_queue[cmd_queue_index_r], code); seen_pointer = strchr(current_command_args, code); // +3 since "G0 " is the shortest prefix
return (strchr_pointer != NULL); //Return True if a character was found return (seen_pointer != NULL); //Return True if a character was found
} }
#define DEFINE_PGM_READ_ANY(type, reader) \ #define DEFINE_PGM_READ_ANY(type, reader) \
@ -1792,6 +1790,13 @@ void gcode_get_destination() {
} }
} }
void unknown_command_error() {
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(current_command);
SERIAL_ECHOPGM("\"\n");
}
/** /**
* G0, G1: Coordinated movement of X Y Z E axes * G0, G1: Coordinated movement of X Y Z E axes
*/ */
@ -2843,7 +2848,7 @@ inline void gcode_G92() {
* M1: // M1 - Conditional stop - Wait for user button press on LCD * M1: // M1 - Conditional stop - Wait for user button press on LCD
*/ */
inline void gcode_M0_M1() { inline void gcode_M0_M1() {
char *src = strchr_pointer + 2; char *args = current_command_args;
millis_t codenum = 0; millis_t codenum = 0;
bool hasP = false, hasS = false; bool hasP = false, hasS = false;
@ -2855,11 +2860,9 @@ inline void gcode_G92() {
codenum = code_value() * 1000; // seconds to wait codenum = code_value() * 1000; // seconds to wait
hasS = codenum > 0; hasS = codenum > 0;
} }
char* starpos = strchr(src, '*');
if (starpos != NULL) *(starpos) = '\0'; if (!hasP && !hasS && *args != '\0')
while (*src == ' ') ++src; lcd_setstatus(args, true);
if (!hasP && !hasS && *src != '\0')
lcd_setstatus(src, true);
else { else {
LCD_MESSAGEPGM(MSG_USERWAIT); LCD_MESSAGEPGM(MSG_USERWAIT);
#if defined(LCD_PROGRESS_BAR) && PROGRESS_MSG_EXPIRE > 0 #if defined(LCD_PROGRESS_BAR) && PROGRESS_MSG_EXPIRE > 0
@ -2932,10 +2935,7 @@ inline void gcode_M17() {
* M23: Select a file * M23: Select a file
*/ */
inline void gcode_M23() { inline void gcode_M23() {
char* codepos = strchr_pointer + 4; card.openFile(current_command_args, true);
char* starpos = strchr(codepos, '*');
if (starpos) *starpos = '\0';
card.openFile(codepos, true);
} }
/** /**
@ -2972,14 +2972,7 @@ inline void gcode_M17() {
* M28: Start SD Write * M28: Start SD Write
*/ */
inline void gcode_M28() { inline void gcode_M28() {
char* codepos = strchr_pointer + 4; card.openFile(current_command_args, false);
char* starpos = strchr(codepos, '*');
if (starpos) {
char* npos = strchr(command_queue[cmd_queue_index_r], 'N');
strchr_pointer = strchr(npos, ' ') + 1;
*(starpos) = '\0';
}
card.openFile(codepos, false);
} }
/** /**
@ -2996,13 +2989,7 @@ inline void gcode_M17() {
inline void gcode_M30() { inline void gcode_M30() {
if (card.cardOK) { if (card.cardOK) {
card.closefile(); card.closefile();
char* starpos = strchr(strchr_pointer + 4, '*'); card.removeFile(current_command_args);
if (starpos) {
char* npos = strchr(command_queue[cmd_queue_index_r], 'N');
strchr_pointer = strchr(npos, ' ') + 1;
*(starpos) = '\0';
}
card.removeFile(strchr_pointer + 4);
} }
} }
@ -3032,23 +3019,18 @@ inline void gcode_M31() {
if (card.sdprinting) if (card.sdprinting)
st_synchronize(); st_synchronize();
char* codepos = strchr_pointer + 4; char* namestartpos = strchr(current_command_args, '!'); // Find ! to indicate filename string start.
char* namestartpos = strchr(codepos, '!'); //find ! to indicate filename string start.
if (!namestartpos) if (!namestartpos)
namestartpos = codepos; //default name position, 4 letters after the M namestartpos = current_command_args; // Default name position, 4 letters after the M
else else
namestartpos++; //to skip the '!' namestartpos++; //to skip the '!'
char* starpos = strchr(codepos, '*'); bool call_procedure = code_seen('P') && (seen_pointer < namestartpos);
if (starpos) *(starpos) = '\0';
bool call_procedure = code_seen('P') && (strchr_pointer < namestartpos);
if (card.cardOK) { if (card.cardOK) {
card.openFile(namestartpos, true, !call_procedure); card.openFile(namestartpos, true, !call_procedure);
if (code_seen('S') && strchr_pointer < namestartpos) // "S" (must occur _before_ the filename!) if (code_seen('S') && seen_pointer < namestartpos) // "S" (must occur _before_ the filename!)
card.setIndex(code_value_short()); card.setIndex(code_value_short());
card.startFileprint(); card.startFileprint();
@ -3061,13 +3043,7 @@ inline void gcode_M31() {
* M928: Start SD Write * M928: Start SD Write
*/ */
inline void gcode_M928() { inline void gcode_M928() {
char* starpos = strchr(strchr_pointer + 5, '*'); card.openLogFile(current_command_args);
if (starpos) {
char* npos = strchr(command_queue[cmd_queue_index_r], 'N');
strchr_pointer = strchr(npos, ' ') + 1;
*(starpos) = '\0';
}
card.openLogFile(strchr_pointer + 5);
} }
#endif // SDSUPPORT #endif // SDSUPPORT
@ -3864,17 +3840,13 @@ inline void gcode_M115() {
SERIAL_PROTOCOLPGM(MSG_M115_REPORT); SERIAL_PROTOCOLPGM(MSG_M115_REPORT);
} }
#ifdef ULTIPANEL
/** /**
* M117: Set LCD Status Message * M117: Set LCD Status Message
*/ */
inline void gcode_M117() { inline void gcode_M117() {
lcd_setstatus(strchr_pointer + 5); lcd_setstatus(current_command_args);
} }
#endif
/** /**
* M119: Output endstop states to serial output * M119: Output endstop states to serial output
*/ */
@ -4161,10 +4133,7 @@ inline void gcode_M206() {
autoretract_enabled = true; autoretract_enabled = true;
break; break;
default: default:
SERIAL_ECHO_START; unknown_command_error();
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(command_queue[cmd_queue_index_r]);
SERIAL_ECHOLNPGM("\"");
return; return;
} }
for (int i=0; i<EXTRUDERS; i++) retracted[i] = false; for (int i=0; i<EXTRUDERS; i++) retracted[i] = false;
@ -5084,8 +5053,7 @@ inline void gcode_M999() {
* *
* F[mm/min] Set the movement feedrate * F[mm/min] Set the movement feedrate
*/ */
inline void gcode_T() { inline void gcode_T(uint8_t tmp_extruder) {
uint16_t tmp_extruder = code_value_short();
if (tmp_extruder >= EXTRUDERS) { if (tmp_extruder >= EXTRUDERS) {
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_CHAR('T'); SERIAL_CHAR('T');
@ -5188,21 +5156,52 @@ inline void gcode_T() {
} }
/** /**
* Process Commands and dispatch them to handlers * Process a single command and dispatch it to its handler
* This is called from the main loop() * This is called from the main loop()
*/ */
void process_next_command() { void process_next_command() {
current_command = command_queue[cmd_queue_index_r];
if ((marlin_debug_flags & DEBUG_ECHO)) { if ((marlin_debug_flags & DEBUG_ECHO)) {
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHOLN(command_queue[cmd_queue_index_r]); SERIAL_ECHOLN(current_command);
} }
if (code_seen('G')) { // Sanitize the current command:
// - Skip leading spaces
// - Bypass N...
// - Overwrite * with nul to mark the end
while (*current_command == ' ') ++current_command;
if (*current_command == 'N' && current_command[1] >= '0' && current_command[1] <= '9') {
while (*current_command != ' ') ++current_command;
while (*current_command == ' ') ++current_command;
}
char *starpos = strchr(current_command, '*'); // * should always be the last parameter
if (starpos) *starpos = '\0';
int codenum = code_value_short(); // Get the command code, which must be G, M, or T
char command_code = *current_command;
switch (codenum) { // The code must have a numeric value
bool code_is_good = (current_command[1] >= '0' && current_command[1] <= '9');
int codenum; // define ahead of goto
// Bail early if there's no code
if (!code_is_good) goto ExitUnknownCommand;
// Args pointer optimizes code_seen, especially those taking XYZEF
// This wastes a little cpu on commands that expect no arguments.
current_command_args = current_command;
while (*current_command_args != ' ') ++current_command_args;
while (*current_command_args == ' ') ++current_command_args;
// Interpret the code int
codenum = code_value_short();
// Handle a known G, M, or T
switch(command_code) {
case 'G': switch (codenum) {
// G0, G1 // G0, G1
case 0: case 0:
@ -5271,11 +5270,12 @@ void process_next_command() {
case 92: // G92 case 92: // G92
gcode_G92(); gcode_G92();
break; break;
}
}
else if (code_seen('M')) { default: code_is_good = false;
switch(code_value_short()) { }
break;
case 'M': switch (codenum) {
#ifdef ULTIPANEL #ifdef ULTIPANEL
case 0: // M0 - Unconditional stop - Wait for user button press on LCD case 0: // M0 - Unconditional stop - Wait for user button press on LCD
case 1: // M1 - Conditional stop - Wait for user button press on LCD case 1: // M1 - Conditional stop - Wait for user button press on LCD
@ -5350,8 +5350,7 @@ void process_next_command() {
case 105: // M105: Read current temperature case 105: // M105: Read current temperature
gcode_M105(); gcode_M105();
return; return; // "ok" already printed
break;
case 109: // M109: Wait for temperature case 109: // M109: Wait for temperature
gcode_M109(); gcode_M109();
@ -5425,13 +5424,9 @@ void process_next_command() {
case 115: // M115: Report capabilities case 115: // M115: Report capabilities
gcode_M115(); gcode_M115();
break; break;
case 117: // M117: Set LCD message text, if possible
#ifdef ULTIPANEL
case 117: // M117: Set LCD message text
gcode_M117(); gcode_M117();
break; break;
#endif
case 114: // M114: Report current position case 114: // M114: Report current position
gcode_M114(); gcode_M114();
break; break;
@ -5701,19 +5696,20 @@ void process_next_command() {
case 999: // M999: Restart after being Stopped case 999: // M999: Restart after being Stopped
gcode_M999(); gcode_M999();
break; break;
default: code_is_good = false;
} }
break;
case 'T':
gcode_T(codenum);
break;
} }
else if (code_seen('T')) { ExitUnknownCommand:
gcode_T();
}
else { // Still unknown command? Throw an error
SERIAL_ECHO_START; if (!code_is_good) unknown_command_error();
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(command_queue[cmd_queue_index_r]);
SERIAL_ECHOLNPGM("\"");
}
ok_to_send(); ok_to_send();
} }