From 9dc04e27c6d700befe9b193126dd297cffb39a01 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 00:14:58 +0100 Subject: [PATCH 01/19] canon: move the NURBS math helpers to nurbs_math.hh The nurbs_* functions evaluate G5.2/G6.2 curves. They are plain math used by the canon implementations, not canonical commands, so they do not belong in the interface the interpreter calls. Drop the three declarations that were listed twice, and alpha_finder, which was declared but never defined. --- src/emc/nml_intf/canon.hh | 76 ------------------ src/emc/nml_intf/nurbs_math.hh | 80 +++++++++++++++++++ src/emc/rs274ngc/gcodemodule.cc | 1 + .../rs274ngc/nurbs_additional_functions.cc | 1 + src/emc/task/emccanon.cc | 1 + tests/interp/compile/use-rs274.cc | 1 + 6 files changed, 84 insertions(+), 76 deletions(-) create mode 100644 src/emc/nml_intf/nurbs_math.hh diff --git a/src/emc/nml_intf/canon.hh b/src/emc/nml_intf/canon.hh index 9a0a4de911c..a67c0c8b8b4 100644 --- a/src/emc/nml_intf/canon.hh +++ b/src/emc/nml_intf/canon.hh @@ -522,80 +522,6 @@ extern void STRAIGHT_FEED(int lineno, double a, double b, double c, double u, double v, double w); -/* Additional functions needed to calculate nurbs G5 points */ - -extern std::vector nurbs_G5_knot_vector_creator(unsigned int n, unsigned int k); - -extern double nurbs_G5_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector); - -extern double nurbs_G5_Rden(double u, unsigned int k, - const std::vector& nurbs_control_points, - const std::vector& knot_vector); - -extern NURBS_PLANE_POINT nurbs_G5_point(double u, unsigned int k, - const std::vector& nurbs_control_points, - const std::vector& knot_vector); - -extern NURBS_PLANE_POINT nurbs_G5_tangent(double u, unsigned int k, - const std::vector& nurbs_control_points, - const std::vector& knot_vector); - -/* Additional functions needed to calculate nurbs points G_6_2*/ - -extern std::vector nurbs_g6_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& nurbs_control_points); - -std::vector nurbs_interval_span_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& knot_vector_); - -extern double nurbs_lderv(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -extern double nurbs_Sa1_b1_length_(double a1, double b1, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -extern std::vector nurbs_lenght_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); - -extern double nurbs_lenght_tot(int j, const std::vector& span_knot_vector, const std::vector& lenght_vector); - -extern double nurbs_lenght_l_u(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector, const std::vector& lenght_vector); - -extern std::vector nurbs_Du_span_knot_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); - -extern std::vector nurbs_costant_crator(const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& Du_span_knot_vector); - -extern double nurbs_uj_l(double l, const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& nurbs_costant); - -/* Funzioni di supporto all'algoritmo di suddivisione */ -extern std::vector nurbs_G6_new_control_point_nurbs1(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); - -extern std::vector nurbs_G6_new_control_point_nurbs2(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); - -extern std::vector nurbs_G6_knot_vector_new_creator_sgment(unsigned int k, const std::vector& nurbs_control_points); -/* ... */ - -extern double nurbs_G6_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector_); - -extern std::vector< std::vector > nurbs_G6_Nmix_creator(double u, unsigned int k, double n, const std::vector& knot_vector_); - -extern double nurbs_G6_Rden(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -extern NURBS_PLANE_POINT nurbs_G6_point(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -extern double nurbs_G6_Rdenx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); - -extern NURBS_PLANE_POINT nurbs_G6_pointx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); - -extern NURBS_PLANE_POINT nurbs_G6_point_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -extern NURBS_PLANE_POINT nurbs_G6_tangent_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); - -/*Funzioni di supporto all'algoritmo di suddivisione********************************************************************/ -extern std::vector nurbs_G6_new_control_point_nurbs1(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); - -extern std::vector nurbs_G6_new_control_point_nurbs2(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); - -extern std::vector nurbs_G6_knot_vector_new_creator_sgment(unsigned int k, const std::vector& nurbs_control_points); - -/* End of NURBS functions*/ - - /* Canon calls */ extern void NURBS_G5_FEED(int lineno, const std::vector& nurbs_control_points, unsigned int nurbs_order, CANON_PLANE plane); @@ -606,8 +532,6 @@ extern void NURBS_G5_FEED(int lineno, const std::vector& nu extern void NURBS_G6_FEED(int lineno, const std::vector& nurbs_control_points, unsigned int k, double feedrate, int l, CANON_PLANE plane); // this is for G6xx -extern double alpha_finder(double dx, double dy); - /********************************************************************************************************************/ diff --git a/src/emc/nml_intf/nurbs_math.hh b/src/emc/nml_intf/nurbs_math.hh new file mode 100644 index 00000000000..790984c1e4a --- /dev/null +++ b/src/emc/nml_intf/nurbs_math.hh @@ -0,0 +1,80 @@ +/******************************************************************** +* Description: nurbs_math.hh +* Math helpers for evaluating the NURBS curves of G5.2 and G6.2. +* They are plain functions, not canonical commands; the canon +* implementations use them to turn NURBS_G5_FEED/NURBS_G6_FEED +* into line segments. +* +* License: GPL Version 2 +********************************************************************/ +#ifndef NURBS_MATH_HH +#define NURBS_MATH_HH + +#include +#include "canon.hh" // NURBS_CONTROL_POINT, NURBS_G6_CONTROL_POINT, NURBS_PLANE_POINT + +/* Additional functions needed to calculate nurbs G5 points */ + +extern std::vector nurbs_G5_knot_vector_creator(unsigned int n, unsigned int k); + +extern double nurbs_G5_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector); + +extern double nurbs_G5_Rden(double u, unsigned int k, + const std::vector& nurbs_control_points, + const std::vector& knot_vector); + +extern NURBS_PLANE_POINT nurbs_G5_point(double u, unsigned int k, + const std::vector& nurbs_control_points, + const std::vector& knot_vector); + +extern NURBS_PLANE_POINT nurbs_G5_tangent(double u, unsigned int k, + const std::vector& nurbs_control_points, + const std::vector& knot_vector); + +/* Additional functions needed to calculate nurbs points G_6_2*/ + +extern std::vector nurbs_g6_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& nurbs_control_points); + +std::vector nurbs_interval_span_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& knot_vector_); + +extern double nurbs_lderv(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +extern double nurbs_Sa1_b1_length_(double a1, double b1, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +extern std::vector nurbs_lenght_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); + +extern double nurbs_lenght_tot(int j, const std::vector& span_knot_vector, const std::vector& lenght_vector); + +extern double nurbs_lenght_l_u(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector, const std::vector& lenght_vector); + +extern std::vector nurbs_Du_span_knot_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); + +extern std::vector nurbs_costant_crator(const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& Du_span_knot_vector); + +extern double nurbs_uj_l(double l, const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& nurbs_costant); + +/* Funzioni di supporto all'algoritmo di suddivisione */ +extern std::vector nurbs_G6_new_control_point_nurbs1(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); + +extern std::vector nurbs_G6_new_control_point_nurbs2(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); + +extern std::vector nurbs_G6_knot_vector_new_creator_sgment(unsigned int k, const std::vector& nurbs_control_points); +/* ... */ + +extern double nurbs_G6_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector_); + +extern std::vector< std::vector > nurbs_G6_Nmix_creator(double u, unsigned int k, double n, const std::vector& knot_vector_); + +extern double nurbs_G6_Rden(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +extern NURBS_PLANE_POINT nurbs_G6_point(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +extern double nurbs_G6_Rdenx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); + +extern NURBS_PLANE_POINT nurbs_G6_pointx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); + +extern NURBS_PLANE_POINT nurbs_G6_point_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +extern NURBS_PLANE_POINT nurbs_G6_tangent_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); + +#endif diff --git a/src/emc/rs274ngc/gcodemodule.cc b/src/emc/rs274ngc/gcodemodule.cc index f725a708dff..c3fe483256d 100644 --- a/src/emc/rs274ngc/gcodemodule.cc +++ b/src/emc/rs274ngc/gcodemodule.cc @@ -57,6 +57,7 @@ #include "rs274ngc_interp.hh" #include "nml_intf/interp_return.hh" #include "nml_intf/canon.hh" +#include "nml_intf/nurbs_math.hh" #include int _task = 0; // control preview behaviour when remapping diff --git a/src/emc/rs274ngc/nurbs_additional_functions.cc b/src/emc/rs274ngc/nurbs_additional_functions.cc index c4825d6aa01..8b4a4340ccb 100644 --- a/src/emc/rs274ngc/nurbs_additional_functions.cc +++ b/src/emc/rs274ngc/nurbs_additional_functions.cc @@ -13,6 +13,7 @@ #include #include #include "nml_intf/canon.hh" +#include "nml_intf/nurbs_math.hh" static void unit(NURBS_PLANE_POINT &p) { diff --git a/src/emc/task/emccanon.cc b/src/emc/task/emccanon.cc index 5ad736c59e8..bb1c814c527 100644 --- a/src/emc/task/emccanon.cc +++ b/src/emc/task/emccanon.cc @@ -58,6 +58,7 @@ #include "nml_intf/emc_nml.hh" #include // SWITCHKINS_MAX_TYPES #include "nml_intf/canon.hh" +#include "nml_intf/nurbs_math.hh" #include "nml_intf/canon_position.hh" // data type for a machine position #include "nml_intf/interpl_nml.hh" // interp_list #include "nml_intf/emcglb.h" // TRAJ_MAX_VELOCITY diff --git a/tests/interp/compile/use-rs274.cc b/tests/interp/compile/use-rs274.cc index 45e75382484..f5e386312f5 100644 --- a/tests/interp/compile/use-rs274.cc +++ b/tests/interp/compile/use-rs274.cc @@ -18,6 +18,7 @@ #include "linuxcnc.h" // LINELEN #include "rs274ngc.hh" #include "canon.hh" +#include "nurbs_math.hh" static void read_execute(InterpBase *b, const char *line) { fprintf(stderr, "> %s\n", line); From 3aaef5bbadf1aa2e38cbfcad07e1fcd57d0791dc Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 00:28:50 +0100 Subject: [PATCH 02/19] canon: return whole poses from the position and tool offset queries GET_EXTERNAL_POSITION_*, GET_EXTERNAL_PROBE_POSITION_* and GET_EXTERNAL_TOOL_LENGTH_*OFFSET were nine functions each, one per axis. The interpreter always reads all nine together, and emccanon already built the whole pose for every single-axis call. Replace the 27 functions with CANON_POSITION GET_EXTERNAL_POSITION(); CANON_POSITION GET_EXTERNAL_PROBE_POSITION(); EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET(); so the interpreter makes one call where it made nine. The emccanon Python module keeps the per-axis names as wrappers, so remaps that call them, like stdglue.py, keep working. --- src/emc/nml_intf/canon.hh | 53 ++------ src/emc/rs274ngc/canonmodule.cc | 55 ++++----- src/emc/rs274ngc/gcodemodule.cc | 60 +++------- src/emc/rs274ngc/interp_convert.cc | 20 +--- src/emc/rs274ngc/interp_internal.cc | 38 +++--- src/emc/rs274ngc/rs274ngc_pre.cc | 19 +-- src/emc/sai/saicanon.cc | 166 ++++--------------------- src/emc/task/emccanon.cc | 180 ++-------------------------- tests/interp/compile/use-rs274.cc | 30 +---- 9 files changed, 120 insertions(+), 501 deletions(-) diff --git a/src/emc/nml_intf/canon.hh b/src/emc/nml_intf/canon.hh index a67c0c8b8b4..aeb718a513d 100644 --- a/src/emc/nml_intf/canon.hh +++ b/src/emc/nml_intf/canon.hh @@ -903,45 +903,14 @@ extern void SET_PARAMETER_FILE_NAME(const char *name); // returns the currently active plane extern CANON_PLANE GET_EXTERNAL_PLANE(); -// returns the current a-axis position -extern double GET_EXTERNAL_POSITION_A(); +// returns the current position of all nine axes, in program units +// and the current work coordinate system +extern CANON_POSITION GET_EXTERNAL_POSITION(); -// returns the current b-axis position -extern double GET_EXTERNAL_POSITION_B(); -// returns the current c-axis position -extern double GET_EXTERNAL_POSITION_C(); - -// returns the current x-axis position -extern double GET_EXTERNAL_POSITION_X(); - -// returns the current y-axis position -extern double GET_EXTERNAL_POSITION_Y(); - -// returns the current z-axis position -extern double GET_EXTERNAL_POSITION_Z(); - -// returns the current u-axis position -extern double GET_EXTERNAL_POSITION_U(); - -// returns the current v-axis position -extern double GET_EXTERNAL_POSITION_V(); - -// returns the current w-axis position -extern double GET_EXTERNAL_POSITION_W(); - - -// Returns the position of the specified axis at the last probe trip, +// Returns the position of all axes at the last probe trip, // in the current work coordinate system. -extern double GET_EXTERNAL_PROBE_POSITION_A(); -extern double GET_EXTERNAL_PROBE_POSITION_B(); -extern double GET_EXTERNAL_PROBE_POSITION_C(); -extern double GET_EXTERNAL_PROBE_POSITION_X(); -extern double GET_EXTERNAL_PROBE_POSITION_Y(); -extern double GET_EXTERNAL_PROBE_POSITION_Z(); -extern double GET_EXTERNAL_PROBE_POSITION_U(); -extern double GET_EXTERNAL_PROBE_POSITION_V(); -extern double GET_EXTERNAL_PROBE_POSITION_W(); +extern CANON_POSITION GET_EXTERNAL_PROBE_POSITION(); // Returns the value for any analog non-contact probing. extern double GET_EXTERNAL_PROBE_VALUE(); @@ -959,16 +928,8 @@ extern double GET_EXTERNAL_SPEED(int spindle); // Returns the system value for direction of spindle turning extern CANON_DIRECTION GET_EXTERNAL_SPINDLE(int spindle); -// returns current tool length offset -extern double GET_EXTERNAL_TOOL_LENGTH_XOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_YOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_AOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_BOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_COFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_UOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_VOFFSET(); -extern double GET_EXTERNAL_TOOL_LENGTH_WOFFSET(); +// returns current tool length offset, in program units +extern EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET(); // Returns the system value for the carousel slot in which the tool // currently in the spindle belongs. Return value zero means there is no diff --git a/src/emc/rs274ngc/canonmodule.cc b/src/emc/rs274ngc/canonmodule.cc index 66138b48bfb..11bbd5b3898 100644 --- a/src/emc/rs274ngc/canonmodule.cc +++ b/src/emc/rs274ngc/canonmodule.cc @@ -143,24 +143,34 @@ BOOST_PYTHON_MODULE(emccanon) { &GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE); def("GET_EXTERNAL_PARAMETER_FILE_NAME",&GET_EXTERNAL_PARAMETER_FILE_NAME); def("GET_EXTERNAL_PLANE",&GET_EXTERNAL_PLANE); - def("GET_EXTERNAL_POSITION_A",&GET_EXTERNAL_POSITION_A); - def("GET_EXTERNAL_POSITION_B",&GET_EXTERNAL_POSITION_B); - def("GET_EXTERNAL_POSITION_C",&GET_EXTERNAL_POSITION_C); - def("GET_EXTERNAL_POSITION_U",&GET_EXTERNAL_POSITION_U); - def("GET_EXTERNAL_POSITION_V",&GET_EXTERNAL_POSITION_V); - def("GET_EXTERNAL_POSITION_W",&GET_EXTERNAL_POSITION_W); - def("GET_EXTERNAL_POSITION_X",&GET_EXTERNAL_POSITION_X); - def("GET_EXTERNAL_POSITION_Y",&GET_EXTERNAL_POSITION_Y); - def("GET_EXTERNAL_POSITION_Z",&GET_EXTERNAL_POSITION_Z); - def("GET_EXTERNAL_PROBE_POSITION_A",&GET_EXTERNAL_PROBE_POSITION_A); - def("GET_EXTERNAL_PROBE_POSITION_B",&GET_EXTERNAL_PROBE_POSITION_B); - def("GET_EXTERNAL_PROBE_POSITION_C",&GET_EXTERNAL_PROBE_POSITION_C); - def("GET_EXTERNAL_PROBE_POSITION_U",&GET_EXTERNAL_PROBE_POSITION_U); - def("GET_EXTERNAL_PROBE_POSITION_V",&GET_EXTERNAL_PROBE_POSITION_V); - def("GET_EXTERNAL_PROBE_POSITION_W",&GET_EXTERNAL_PROBE_POSITION_W); - def("GET_EXTERNAL_PROBE_POSITION_X",&GET_EXTERNAL_PROBE_POSITION_X); - def("GET_EXTERNAL_PROBE_POSITION_Y",&GET_EXTERNAL_PROBE_POSITION_Y); - def("GET_EXTERNAL_PROBE_POSITION_Z",&GET_EXTERNAL_PROBE_POSITION_Z); + // The canon interface returns whole poses; Python keeps the per-axis names. + def("GET_EXTERNAL_POSITION_X", +[]() { return GET_EXTERNAL_POSITION().x; }); + def("GET_EXTERNAL_POSITION_Y", +[]() { return GET_EXTERNAL_POSITION().y; }); + def("GET_EXTERNAL_POSITION_Z", +[]() { return GET_EXTERNAL_POSITION().z; }); + def("GET_EXTERNAL_POSITION_A", +[]() { return GET_EXTERNAL_POSITION().a; }); + def("GET_EXTERNAL_POSITION_B", +[]() { return GET_EXTERNAL_POSITION().b; }); + def("GET_EXTERNAL_POSITION_C", +[]() { return GET_EXTERNAL_POSITION().c; }); + def("GET_EXTERNAL_POSITION_U", +[]() { return GET_EXTERNAL_POSITION().u; }); + def("GET_EXTERNAL_POSITION_V", +[]() { return GET_EXTERNAL_POSITION().v; }); + def("GET_EXTERNAL_POSITION_W", +[]() { return GET_EXTERNAL_POSITION().w; }); + def("GET_EXTERNAL_PROBE_POSITION_X", +[]() { return GET_EXTERNAL_PROBE_POSITION().x; }); + def("GET_EXTERNAL_PROBE_POSITION_Y", +[]() { return GET_EXTERNAL_PROBE_POSITION().y; }); + def("GET_EXTERNAL_PROBE_POSITION_Z", +[]() { return GET_EXTERNAL_PROBE_POSITION().z; }); + def("GET_EXTERNAL_PROBE_POSITION_A", +[]() { return GET_EXTERNAL_PROBE_POSITION().a; }); + def("GET_EXTERNAL_PROBE_POSITION_B", +[]() { return GET_EXTERNAL_PROBE_POSITION().b; }); + def("GET_EXTERNAL_PROBE_POSITION_C", +[]() { return GET_EXTERNAL_PROBE_POSITION().c; }); + def("GET_EXTERNAL_PROBE_POSITION_U", +[]() { return GET_EXTERNAL_PROBE_POSITION().u; }); + def("GET_EXTERNAL_PROBE_POSITION_V", +[]() { return GET_EXTERNAL_PROBE_POSITION().v; }); + def("GET_EXTERNAL_PROBE_POSITION_W", +[]() { return GET_EXTERNAL_PROBE_POSITION().w; }); + def("GET_EXTERNAL_TOOL_LENGTH_XOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().tran.x; }); + def("GET_EXTERNAL_TOOL_LENGTH_YOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().tran.y; }); + def("GET_EXTERNAL_TOOL_LENGTH_ZOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().tran.z; }); + def("GET_EXTERNAL_TOOL_LENGTH_AOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().a; }); + def("GET_EXTERNAL_TOOL_LENGTH_BOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().b; }); + def("GET_EXTERNAL_TOOL_LENGTH_COFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().c; }); + def("GET_EXTERNAL_TOOL_LENGTH_UOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().u; }); + def("GET_EXTERNAL_TOOL_LENGTH_VOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().v; }); + def("GET_EXTERNAL_TOOL_LENGTH_WOFFSET", +[]() { return GET_EXTERNAL_TOOL_LENGTH_OFFSET().w; }); def("GET_EXTERNAL_PROBE_TRIPPED_VALUE",&GET_EXTERNAL_PROBE_TRIPPED_VALUE); def("GET_EXTERNAL_PROBE_VALUE",&GET_EXTERNAL_PROBE_VALUE); def("GET_EXTERNAL_QUEUE_EMPTY",&GET_EXTERNAL_QUEUE_EMPTY); @@ -169,15 +179,6 @@ BOOST_PYTHON_MODULE(emccanon) { def("GET_EXTERNAL_SPINDLE",&GET_EXTERNAL_SPINDLE); def("GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE",&GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE); def("GET_EXTERNAL_TC_FAULT",&GET_EXTERNAL_TC_FAULT); - def("GET_EXTERNAL_TOOL_LENGTH_AOFFSET",&GET_EXTERNAL_TOOL_LENGTH_AOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_BOFFSET",&GET_EXTERNAL_TOOL_LENGTH_BOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_COFFSET",&GET_EXTERNAL_TOOL_LENGTH_COFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_UOFFSET",&GET_EXTERNAL_TOOL_LENGTH_UOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_VOFFSET",&GET_EXTERNAL_TOOL_LENGTH_VOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_WOFFSET",&GET_EXTERNAL_TOOL_LENGTH_WOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_XOFFSET",&GET_EXTERNAL_TOOL_LENGTH_XOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_YOFFSET",&GET_EXTERNAL_TOOL_LENGTH_YOFFSET); - def("GET_EXTERNAL_TOOL_LENGTH_ZOFFSET",&GET_EXTERNAL_TOOL_LENGTH_ZOFFSET); def("GET_EXTERNAL_TOOL_SLOT",&GET_EXTERNAL_TOOL_SLOT); def("GET_EXTERNAL_TOOL_TABLE",&GET_EXTERNAL_TOOL_TABLE); def("GET_EXTERNAL_AXIS_MAX_VELOCITY",&GET_EXTERNAL_AXIS_MAX_VELOCITY); diff --git a/src/emc/rs274ngc/gcodemodule.cc b/src/emc/rs274ngc/gcodemodule.cc index c3fe483256d..f86b53c0a20 100644 --- a/src/emc/rs274ngc/gcodemodule.cc +++ b/src/emc/rs274ngc/gcodemodule.cc @@ -734,26 +734,22 @@ void RIGID_TAP(int line_number, } double GET_EXTERNAL_MOTION_CONTROL_TOLERANCE() { return 0.1; } double GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE() { return 0.1; } -double GET_EXTERNAL_PROBE_POSITION_X() { return parse_state.pos[P9_X]; } -double GET_EXTERNAL_PROBE_POSITION_Y() { return parse_state.pos[P9_Y]; } -double GET_EXTERNAL_PROBE_POSITION_Z() { return parse_state.pos[P9_Z]; } -double GET_EXTERNAL_PROBE_POSITION_A() { return parse_state.pos[P9_A]; } -double GET_EXTERNAL_PROBE_POSITION_B() { return parse_state.pos[P9_B]; } -double GET_EXTERNAL_PROBE_POSITION_C() { return parse_state.pos[P9_C]; } -double GET_EXTERNAL_PROBE_POSITION_U() { return parse_state.pos[P9_U]; } -double GET_EXTERNAL_PROBE_POSITION_V() { return parse_state.pos[P9_V]; } -double GET_EXTERNAL_PROBE_POSITION_W() { return parse_state.pos[P9_W]; } double GET_EXTERNAL_PROBE_VALUE() { return 0.0; } int GET_EXTERNAL_PROBE_TRIPPED_VALUE() { return 0; } -double GET_EXTERNAL_POSITION_X() { return parse_state.pos[P9_X]; } -double GET_EXTERNAL_POSITION_Y() { return parse_state.pos[P9_Y]; } -double GET_EXTERNAL_POSITION_Z() { return parse_state.pos[P9_Z]; } -double GET_EXTERNAL_POSITION_A() { return parse_state.pos[P9_A]; } -double GET_EXTERNAL_POSITION_B() { return parse_state.pos[P9_B]; } -double GET_EXTERNAL_POSITION_C() { return parse_state.pos[P9_C]; } -double GET_EXTERNAL_POSITION_U() { return parse_state.pos[P9_U]; } -double GET_EXTERNAL_POSITION_V() { return parse_state.pos[P9_V]; } -double GET_EXTERNAL_POSITION_W() { return parse_state.pos[P9_W]; } +CANON_POSITION GET_EXTERNAL_POSITION() { + CANON_POSITION pos; + pos.x = parse_state.pos[P9_X]; + pos.y = parse_state.pos[P9_Y]; + pos.z = parse_state.pos[P9_Z]; + pos.a = parse_state.pos[P9_A]; + pos.b = parse_state.pos[P9_B]; + pos.c = parse_state.pos[P9_C]; + pos.u = parse_state.pos[P9_U]; + pos.v = parse_state.pos[P9_V]; + pos.w = parse_state.pos[P9_W]; + return pos; +} +CANON_POSITION GET_EXTERNAL_PROBE_POSITION() { return GET_EXTERNAL_POSITION(); } void INIT_CANON() {} void SET_PARAMETER_FILE_NAME(const char *name) @@ -848,32 +844,8 @@ int GET_EXTERNAL_AXIS_MASK() { return mask; } -double GET_EXTERNAL_TOOL_LENGTH_XOFFSET() { - return parse_state.tool_offset.tran.x; -} -double GET_EXTERNAL_TOOL_LENGTH_YOFFSET() { - return parse_state.tool_offset.tran.y; -} -double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET() { - return parse_state.tool_offset.tran.z; -} -double GET_EXTERNAL_TOOL_LENGTH_AOFFSET() { - return parse_state.tool_offset.a; -} -double GET_EXTERNAL_TOOL_LENGTH_BOFFSET() { - return parse_state.tool_offset.b; -} -double GET_EXTERNAL_TOOL_LENGTH_COFFSET() { - return parse_state.tool_offset.c; -} -double GET_EXTERNAL_TOOL_LENGTH_UOFFSET() { - return parse_state.tool_offset.u; -} -double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() { - return parse_state.tool_offset.v; -} -double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { - return parse_state.tool_offset.w; +EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET() { + return parse_state.tool_offset; } // The unit constants, answered by the parse's protocol: the callback protocol diff --git a/src/emc/rs274ngc/interp_convert.cc b/src/emc/rs274ngc/interp_convert.cc index 379b21eaba6..0e11093b768 100644 --- a/src/emc/rs274ngc/interp_convert.cc +++ b/src/emc/rs274ngc/interp_convert.cc @@ -3539,15 +3539,7 @@ int Interp::convert_length_units(int g_code, //!< g_code being executed (mus scale_linear_axes(settings, INCH_PER_MM); - settings->tool_offset.tran.x = GET_EXTERNAL_TOOL_LENGTH_XOFFSET(); - settings->tool_offset.tran.y = GET_EXTERNAL_TOOL_LENGTH_YOFFSET(); - settings->tool_offset.tran.z = GET_EXTERNAL_TOOL_LENGTH_ZOFFSET(); - settings->tool_offset.a = GET_EXTERNAL_TOOL_LENGTH_AOFFSET(); - settings->tool_offset.b = GET_EXTERNAL_TOOL_LENGTH_BOFFSET(); - settings->tool_offset.c = GET_EXTERNAL_TOOL_LENGTH_COFFSET(); - settings->tool_offset.u = GET_EXTERNAL_TOOL_LENGTH_UOFFSET(); - settings->tool_offset.v = GET_EXTERNAL_TOOL_LENGTH_VOFFSET(); - settings->tool_offset.w = GET_EXTERNAL_TOOL_LENGTH_WOFFSET(); + settings->tool_offset = GET_EXTERNAL_TOOL_LENGTH_OFFSET(); settings->feed_rate = GET_EXTERNAL_FEED_RATE(); settings->tolerance = GET_EXTERNAL_MOTION_CONTROL_TOLERANCE(); settings->naivecam_tolerance = @@ -3580,15 +3572,7 @@ int Interp::convert_length_units(int g_code, //!< g_code being executed (mus scale_linear_axes(settings, MM_PER_INCH); - settings->tool_offset.tran.x = GET_EXTERNAL_TOOL_LENGTH_XOFFSET(); - settings->tool_offset.tran.y = GET_EXTERNAL_TOOL_LENGTH_YOFFSET(); - settings->tool_offset.tran.z = GET_EXTERNAL_TOOL_LENGTH_ZOFFSET(); - settings->tool_offset.a = GET_EXTERNAL_TOOL_LENGTH_AOFFSET(); - settings->tool_offset.b = GET_EXTERNAL_TOOL_LENGTH_BOFFSET(); - settings->tool_offset.c = GET_EXTERNAL_TOOL_LENGTH_COFFSET(); - settings->tool_offset.u = GET_EXTERNAL_TOOL_LENGTH_UOFFSET(); - settings->tool_offset.v = GET_EXTERNAL_TOOL_LENGTH_VOFFSET(); - settings->tool_offset.w = GET_EXTERNAL_TOOL_LENGTH_WOFFSET(); + settings->tool_offset = GET_EXTERNAL_TOOL_LENGTH_OFFSET(); settings->feed_rate = GET_EXTERNAL_FEED_RATE(); settings->tolerance = GET_EXTERNAL_MOTION_CONTROL_TOLERANCE(); settings->naivecam_tolerance = diff --git a/src/emc/rs274ngc/interp_internal.cc b/src/emc/rs274ngc/interp_internal.cc index bcccebe27c2..b107d221bc7 100644 --- a/src/emc/rs274ngc/interp_internal.cc +++ b/src/emc/rs274ngc/interp_internal.cc @@ -415,15 +415,16 @@ int Interp::precedence(int an_operator) int Interp::refresh_actual_position(setup_pointer settings) { - settings->current_x = GET_EXTERNAL_POSITION_X(); - settings->current_y = GET_EXTERNAL_POSITION_Y(); - settings->current_z = GET_EXTERNAL_POSITION_Z(); - settings->AA_current = GET_EXTERNAL_POSITION_A(); - settings->BB_current = GET_EXTERNAL_POSITION_B(); - settings->CC_current = GET_EXTERNAL_POSITION_C(); - settings->u_current = GET_EXTERNAL_POSITION_U(); - settings->v_current = GET_EXTERNAL_POSITION_V(); - settings->w_current = GET_EXTERNAL_POSITION_W(); + const CANON_POSITION pos = GET_EXTERNAL_POSITION(); + settings->current_x = pos.x; + settings->current_y = pos.y; + settings->current_z = pos.z; + settings->AA_current = pos.a; + settings->BB_current = pos.b; + settings->CC_current = pos.c; + settings->u_current = pos.u; + settings->v_current = pos.v; + settings->w_current = pos.w; return INTERP_OK; } @@ -448,41 +449,42 @@ int Interp::set_probe_data(setup_pointer settings) //!< pointer to machine { double a, b, c, u, v, w; refresh_actual_position(settings); - settings->parameters[5061] = GET_EXTERNAL_PROBE_POSITION_X(); - settings->parameters[5062] = GET_EXTERNAL_PROBE_POSITION_Y(); - settings->parameters[5063] = GET_EXTERNAL_PROBE_POSITION_Z(); + const CANON_POSITION probe = GET_EXTERNAL_PROBE_POSITION(); + settings->parameters[5061] = probe.x; + settings->parameters[5062] = probe.y; + settings->parameters[5063] = probe.z; - a = GET_EXTERNAL_PROBE_POSITION_A(); + a = probe.a; if(settings->axis_wrapped[AXIS_A] || settings->axis_rotary_modulo[AXIS_A]) { a = wrap_rotary_to_360(a); } settings->parameters[5064] = a; - b = GET_EXTERNAL_PROBE_POSITION_B(); + b = probe.b; if(settings->axis_wrapped[AXIS_B] || settings->axis_rotary_modulo[AXIS_B]) { b = wrap_rotary_to_360(b); } settings->parameters[5065] = b; - c = GET_EXTERNAL_PROBE_POSITION_C(); + c = probe.c; if(settings->axis_wrapped[AXIS_C] || settings->axis_rotary_modulo[AXIS_C]) { c = wrap_rotary_to_360(c); } settings->parameters[5066] = c; - u = GET_EXTERNAL_PROBE_POSITION_U(); + u = probe.u; if(settings->axis_wrapped[AXIS_U] || settings->axis_rotary_modulo[AXIS_U]) { u = wrap_rotary_to_360(u); } settings->parameters[5067] = u; - v = GET_EXTERNAL_PROBE_POSITION_V(); + v = probe.v; if(settings->axis_wrapped[AXIS_V] || settings->axis_rotary_modulo[AXIS_V]) { v = wrap_rotary_to_360(v); } settings->parameters[5068] = v; - w = GET_EXTERNAL_PROBE_POSITION_W(); + w = probe.w; if(settings->axis_wrapped[AXIS_W] || settings->axis_rotary_modulo[AXIS_W]) { w = wrap_rotary_to_360(w); } diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index f420af7b336..780890fddec 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -2110,18 +2110,19 @@ int Interp::synch() { char file_name[LINELEN]; - _setup.current_x = GET_EXTERNAL_POSITION_X(); - _setup.current_y = GET_EXTERNAL_POSITION_Y(); - _setup.current_z = GET_EXTERNAL_POSITION_Z(); + const CANON_POSITION pos = GET_EXTERNAL_POSITION(); + _setup.current_x = pos.x; + _setup.current_y = pos.y; + _setup.current_z = pos.z; _setup.control_mode = GET_EXTERNAL_MOTION_CONTROL_MODE(); _setup.tolerance = GET_EXTERNAL_MOTION_CONTROL_TOLERANCE(); _setup.naivecam_tolerance = GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE(); - _setup.AA_current = GET_EXTERNAL_POSITION_A(); - _setup.BB_current = GET_EXTERNAL_POSITION_B(); - _setup.CC_current = GET_EXTERNAL_POSITION_C(); - _setup.u_current = GET_EXTERNAL_POSITION_U(); - _setup.v_current = GET_EXTERNAL_POSITION_V(); - _setup.w_current = GET_EXTERNAL_POSITION_W(); + _setup.AA_current = pos.a; + _setup.BB_current = pos.b; + _setup.CC_current = pos.c; + _setup.u_current = pos.u; + _setup.v_current = pos.v; + _setup.w_current = pos.w; _setup.control_mode = GET_EXTERNAL_MOTION_CONTROL_MODE(); /* misnomer: _setup.current_pocket,selected_pocket diff --git a/src/emc/sai/saicanon.cc b/src/emc/sai/saicanon.cc index 11ea4dfae7e..0a03e45c49b 100644 --- a/src/emc/sai/saicanon.cc +++ b/src/emc/sai/saicanon.cc @@ -843,113 +843,31 @@ CANON_PLANE GET_EXTERNAL_PLANE() return _sai._active_plane; } -/* returns the current a-axis position */ -double GET_EXTERNAL_POSITION_A() +/* returns the current position. XXX fix sai for uvw */ +CANON_POSITION GET_EXTERNAL_POSITION() { - return _sai._program_position_a; + CANON_POSITION pos; // u, v, w stay 0 + pos.x = _sai._program_position_x; + pos.y = _sai._program_position_y; + pos.z = _sai._program_position_z; + pos.a = _sai._program_position_a; + pos.b = _sai._program_position_b; + pos.c = _sai._program_position_c; + return pos; } -/* returns the current b-axis position */ -double GET_EXTERNAL_POSITION_B() -{ - return _sai._program_position_b; -} - -/* returns the current c-axis position */ -double GET_EXTERNAL_POSITION_C() -{ - return _sai._program_position_c; -} - -/* returns the current x-axis position */ -double GET_EXTERNAL_POSITION_X() -{ - return _sai._program_position_x; -} - -/* returns the current y-axis position */ -double GET_EXTERNAL_POSITION_Y() -{ - return _sai._program_position_y; -} - -/* returns the current z-axis position */ -double GET_EXTERNAL_POSITION_Z() -{ - return _sai._program_position_z; -} - -// XXX fix sai for uvw -double GET_EXTERNAL_POSITION_U() -{ - return 0.; -} - -double GET_EXTERNAL_POSITION_V() -{ - return 0.; -} - -double GET_EXTERNAL_POSITION_W() -{ - return 0.; -} - -double GET_EXTERNAL_PROBE_POSITION_U() -{ - return 0.; -} - -double GET_EXTERNAL_PROBE_POSITION_V() -{ - return 0.; -} - -double GET_EXTERNAL_PROBE_POSITION_W() -{ - return 0.; -} - -/* returns the a-axis position at the last probe trip. This is only valid +/* returns the position at the last probe trip. This is only valid once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_A() +CANON_POSITION GET_EXTERNAL_PROBE_POSITION() { - return _sai._probe_position_a; -} - -/* returns the b-axis position at the last probe trip. This is only valid - once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_B() -{ - return _sai._probe_position_b; -} - -/* returns the c-axis position at the last probe trip. This is only valid - once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_C() -{ - return _sai._probe_position_c; -} - -/* returns the x-axis position at the last probe trip. This is only valid - once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_X() -{ - return _sai._probe_position_x; -} - -/* returns the y-axis position at the last probe trip. This is only valid - once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_Y() -{ - return _sai._probe_position_y; -} - -/* returns the z-axis position at the last probe trip. This is only valid - once the probe command has executed to completion. */ -double GET_EXTERNAL_PROBE_POSITION_Z() -{ - return _sai._probe_position_z; + CANON_POSITION pos; // u, v, w stay 0 + pos.x = _sai._probe_position_x; + pos.y = _sai._probe_position_y; + pos.z = _sai._probe_position_z; + pos.a = _sai._probe_position_a; + pos.b = _sai._probe_position_b; + pos.c = _sai._probe_position_c; + return pos; } /* Returns the value for any analog non-contact probing. */ @@ -1073,49 +991,9 @@ void SET_AUX_OUTPUT_VALUE(int index, double value) return; } -double GET_EXTERNAL_TOOL_LENGTH_XOFFSET() -{ - return _sai._tool_offset.tran.x; -} - -double GET_EXTERNAL_TOOL_LENGTH_YOFFSET() -{ - return _sai._tool_offset.tran.y; -} - -double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET() -{ - return _sai._tool_offset.tran.z; -} - -double GET_EXTERNAL_TOOL_LENGTH_AOFFSET() -{ - return _sai._tool_offset.a; -} - -double GET_EXTERNAL_TOOL_LENGTH_BOFFSET() -{ - return _sai._tool_offset.b; -} - -double GET_EXTERNAL_TOOL_LENGTH_COFFSET() -{ - return _sai._tool_offset.c; -} - -double GET_EXTERNAL_TOOL_LENGTH_UOFFSET() -{ - return _sai._tool_offset.u; -} - -double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() -{ - return _sai._tool_offset.v; -} - -double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() +EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET() { - return _sai._tool_offset.w; + return _sai._tool_offset; } void FINISH(void) { diff --git a/src/emc/task/emccanon.cc b/src/emc/task/emccanon.cc index bb1c814c527..1d977d0e12f 100644 --- a/src/emc/task/emccanon.cc +++ b/src/emc/task/emccanon.cc @@ -3452,49 +3452,19 @@ void PROGRAM_END() SIMPLE_COMMAND_(); } -double GET_EXTERNAL_TOOL_LENGTH_XOFFSET() +EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET() { - return TO_PROG_LEN(canon.toolOffset.tran.x); -} - -double GET_EXTERNAL_TOOL_LENGTH_YOFFSET() -{ - return TO_PROG_LEN(canon.toolOffset.tran.y); -} - -double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET() -{ - return TO_PROG_LEN(canon.toolOffset.tran.z); -} - -double GET_EXTERNAL_TOOL_LENGTH_AOFFSET() -{ - return TO_PROG_AX(AXIS_A, canon.toolOffset.a); -} - -double GET_EXTERNAL_TOOL_LENGTH_BOFFSET() -{ - return TO_PROG_AX(AXIS_B, canon.toolOffset.b); -} - -double GET_EXTERNAL_TOOL_LENGTH_COFFSET() -{ - return TO_PROG_AX(AXIS_C, canon.toolOffset.c); -} - -double GET_EXTERNAL_TOOL_LENGTH_UOFFSET() -{ - return TO_PROG_AX(AXIS_U, canon.toolOffset.u); -} - -double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() -{ - return TO_PROG_AX(AXIS_V, canon.toolOffset.v); -} - -double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() -{ - return TO_PROG_AX(AXIS_W, canon.toolOffset.w); + EmcPose off; + off.tran.x = TO_PROG_LEN(canon.toolOffset.tran.x); + off.tran.y = TO_PROG_LEN(canon.toolOffset.tran.y); + off.tran.z = TO_PROG_LEN(canon.toolOffset.tran.z); + off.a = TO_PROG_AX(AXIS_A, canon.toolOffset.a); + off.b = TO_PROG_AX(AXIS_B, canon.toolOffset.b); + off.c = TO_PROG_AX(AXIS_C, canon.toolOffset.c); + off.u = TO_PROG_AX(AXIS_U, canon.toolOffset.u); + off.v = TO_PROG_AX(AXIS_V, canon.toolOffset.v); + off.w = TO_PROG_AX(AXIS_W, canon.toolOffset.w); + return off; } /* @@ -3856,132 +3826,6 @@ void GET_EXTERNAL_PARAMETER_FILE_NAME(char *file_name, /* string: to copy file_name[0] = 0; } -double GET_EXTERNAL_POSITION_X(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.x; -} - -double GET_EXTERNAL_POSITION_Y(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.y; -} - -double GET_EXTERNAL_POSITION_Z(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.z; -} - -double GET_EXTERNAL_POSITION_A(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.a; -} - -double GET_EXTERNAL_POSITION_B(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.b; -} - -double GET_EXTERNAL_POSITION_C(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.c; -} - -double GET_EXTERNAL_POSITION_U(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.u; -} - -double GET_EXTERNAL_POSITION_V(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.v; -} - -double GET_EXTERNAL_POSITION_W(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_POSITION(); - return position.w; -} - -double GET_EXTERNAL_PROBE_POSITION_X(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.x; -} - -double GET_EXTERNAL_PROBE_POSITION_Y(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.y; -} - -double GET_EXTERNAL_PROBE_POSITION_Z(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.z; -} - -double GET_EXTERNAL_PROBE_POSITION_A(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.a; -} - -double GET_EXTERNAL_PROBE_POSITION_B(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.b; -} - -double GET_EXTERNAL_PROBE_POSITION_C(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.c; -} - -double GET_EXTERNAL_PROBE_POSITION_U(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.u; -} - -double GET_EXTERNAL_PROBE_POSITION_V(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.v; -} - -double GET_EXTERNAL_PROBE_POSITION_W(void) -{ - CANON_POSITION position; - position = GET_EXTERNAL_PROBE_POSITION(); - return position.w; -} - CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return canon.motionMode; diff --git a/tests/interp/compile/use-rs274.cc b/tests/interp/compile/use-rs274.cc index f5e386312f5..fbcde627c3c 100644 --- a/tests/interp/compile/use-rs274.cc +++ b/tests/interp/compile/use-rs274.cc @@ -212,38 +212,14 @@ void GET_EXTERNAL_PARAMETER_FILE_NAME(char *filename, int max_size) { snprintf(filename, max_size, "%s", "rs274ngc.var"); } CANON_PLANE GET_EXTERNAL_PLANE() { return CANON_PLANE::XY; } -double GET_EXTERNAL_POSITION_A() { return 0.0; } -double GET_EXTERNAL_POSITION_B() { return 0.0; } -double GET_EXTERNAL_POSITION_C() { return 0.0; } -double GET_EXTERNAL_POSITION_X() { return 0.0; } -double GET_EXTERNAL_POSITION_Y() { return 0.0; } -double GET_EXTERNAL_POSITION_Z() { return 0.0; } -double GET_EXTERNAL_POSITION_U() { return 0.0; } -double GET_EXTERNAL_POSITION_V() { return 0.0; } -double GET_EXTERNAL_POSITION_W() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_A() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_B() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_C() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_X() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_Y() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_Z() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_U() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_V() { return 0.0; } -double GET_EXTERNAL_PROBE_POSITION_W() { return 0.0; } +CANON_POSITION GET_EXTERNAL_POSITION() { return CANON_POSITION(); } +CANON_POSITION GET_EXTERNAL_PROBE_POSITION() { return CANON_POSITION(); } +EmcPose GET_EXTERNAL_TOOL_LENGTH_OFFSET() { return EmcPose(); } double GET_EXTERNAL_PROBE_VALUE() { return 0.0; } int GET_EXTERNAL_PROBE_TRIPPED_VALUE() { return 0; } int GET_EXTERNAL_QUEUE_EMPTY() { return 0; } double GET_EXTERNAL_SPEED(int spindle) { return 0.0; } CANON_DIRECTION GET_EXTERNAL_SPINDLE(int spindle) { return CANON_STOPPED; } -double GET_EXTERNAL_TOOL_LENGTH_XOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_YOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_ZOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_AOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_BOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_COFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_UOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_VOFFSET() { return 0.0; } -double GET_EXTERNAL_TOOL_LENGTH_WOFFSET() { return 0.0; } int GET_EXTERNAL_TOOL_SLOT() { return 0; } int GET_EXTERNAL_SELECTED_TOOL_SLOT() { return 0; } CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int pocket) { CANON_TOOL_TABLE retval; memset(&retval, 0, sizeof(retval)); return retval; } From c5735e013c87458553ad47b2352c7960e8a265e2 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 00:42:42 +0100 Subject: [PATCH 03/19] canon: pass text as std::string_view, return the parameter file name MESSAGE, LOG, LOGOPEN and LOGAPPEND took a non-const char*, so callers holding a const string needed a cast. COMMENT and SET_PARAMETER_FILE_NAME took const char*. All six now take std::string_view, and the code behind them works on the view instead of copying it into a char buffer: - emccanon parses its hot comments (RPY, PROBEOPEN, PROBECLOSE) with string_view and writes the log and probe files through std::ofstream. setString/addString, used only there, are gone. - the preview renderer's comment hook takes the view, and the Python forwards pass it on as is. GET_EXTERNAL_PARAMETER_FILE_NAME filled a caller's buffer and returned an empty name when the name did not fit. It now returns a std::string, so PARAMETER_FILE_NAME_LENGTH is gone and the name has no length limit: the INI reader no longer rejects a long [RS274NGC]PARAMETER_FILE, and sai keeps the name in a std::string. From Python it was not callable at all (it wanted a char buffer) and now returns the name. CANON_ERROR was printf-variadic, but no caller passes arguments; it now takes the message as std::string_view. Callers that need formatting can use fmt::format. std::format is not an option while bookworm's GCC 12 is supported. The emccanon Python module gets small wrappers, since Boost.Python cannot convert to std::string_view. --- src/emc/nml_intf/canon.hh | 23 +++-- src/emc/rs274ngc/canonmodule.cc | 22 +++-- src/emc/rs274ngc/gcode_renderer.cc | 15 ++- src/emc/rs274ngc/gcode_renderer.hh | 2 +- src/emc/rs274ngc/gcodemodule.cc | 26 ++--- src/emc/rs274ngc/gcodemodule.hh | 2 +- src/emc/rs274ngc/rs274ngc_pre.cc | 42 +++----- src/emc/sai/driver.cc | 17 ++-- src/emc/sai/saicanon.cc | 65 ++++-------- src/emc/sai/saicanon.hh | 2 +- src/emc/task/emccanon.cc | 154 ++++++++++------------------- tests/interp/compile/use-rs274.cc | 18 ++-- 12 files changed, 142 insertions(+), 246 deletions(-) diff --git a/src/emc/nml_intf/canon.hh b/src/emc/nml_intf/canon.hh index aeb718a513d..0ccddf78b4a 100644 --- a/src/emc/nml_intf/canon.hh +++ b/src/emc/nml_intf/canon.hh @@ -14,6 +14,8 @@ #define CANON_HH #include // FILE +#include +#include #include #include @@ -661,7 +663,7 @@ controller. An attempt to move an axis while it is clamped should result in an error condition in the controller. */ -extern void COMMENT(const char *s); +extern void COMMENT(std::string_view s); /* This function has no physical effect. If commands are being printed or logged, the comment command is printed or logged, including the string @@ -692,11 +694,11 @@ extern void FLOOD_OFF(); extern void FLOOD_ON(); /* Turn flood coolant on. */ -extern void MESSAGE(char *s); +extern void MESSAGE(std::string_view s); -extern void LOG(char *s); -extern void LOGOPEN(char *s); -extern void LOGAPPEND(char *s); +extern void LOG(std::string_view s); +extern void LOGOPEN(std::string_view filename); +extern void LOGAPPEND(std::string_view filename); extern void LOGCLOSE(); extern void MIST_OFF(); @@ -893,12 +895,10 @@ extern double GET_EXTERNAL_ORIGIN_Z(); */ -// returns nothing but copies the name of the parameter file into -// the filename array, stopping at max_size if the name is longer -// An empty string may be placed in filename. -extern void GET_EXTERNAL_PARAMETER_FILE_NAME(char *filename, int max_size); +// returns the name of the parameter file, or an empty string if none is set +extern std::string GET_EXTERNAL_PARAMETER_FILE_NAME(); -extern void SET_PARAMETER_FILE_NAME(const char *name); +extern void SET_PARAMETER_FILE_NAME(std::string_view name); // returns the currently active plane extern CANON_PLANE GET_EXTERNAL_PLANE(); @@ -974,7 +974,6 @@ extern double GET_EXTERNAL_ANALOG_INPUT(int index, double def); extern int GET_EXTERNAL_AXIS_MASK(); -#define PARAMETER_FILE_NAME_LENGTH 100 #define USER_DEFINED_FUNCTION_NUM 100 typedef void (*USER_DEFINED_FUNCTION_TYPE) (int num, double arg1, @@ -993,7 +992,7 @@ extern void FINISH(void); extern void ON_RESET(void); // expose CANON_ERROR -extern void CANON_ERROR(const char *fmt, ...) __attribute__((format(printf,1,2))); +extern void CANON_ERROR(std::string_view message); extern int GET_EXTERNAL_OFFSET_APPLIED(); extern EmcPose GET_EXTERNAL_OFFSETS(); diff --git a/src/emc/rs274ngc/canonmodule.cc b/src/emc/rs274ngc/canonmodule.cc index 11bbd5b3898..169c77d913a 100644 --- a/src/emc/rs274ngc/canonmodule.cc +++ b/src/emc/rs274ngc/canonmodule.cc @@ -28,14 +28,18 @@ namespace bp = boost::python; #include "rs274ngc_interp.hh" #include "interp_queue.hh" -static void wrap_canon_error(const char *s) +static void wrap_canon_error(const std::string &s) { - if ((s == NULL) || (strlen(s) == 0)) { + if (s.empty()) { return; } - CANON_ERROR("%s", s); + CANON_ERROR(s); } +// Boost.Python has no std::string_view converter. +template +static void wrap_text(const std::string &s) { F(s); } + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" @@ -109,7 +113,7 @@ BOOST_PYTHON_MODULE(emccanon) { def("CLAMP_AXIS",&CLAMP_AXIS); def("CLEAR_AUX_OUTPUT_BIT",&CLEAR_AUX_OUTPUT_BIT); def("CLEAR_MOTION_OUTPUT_BIT",&CLEAR_MOTION_OUTPUT_BIT); - def("COMMENT",&COMMENT); + def("COMMENT",&wrap_text); def("DISABLE_ADAPTIVE_FEED",&DISABLE_ADAPTIVE_FEED); def("DISABLE_FEED_HOLD",&DISABLE_FEED_HOLD); def("DISABLE_FEED_OVERRIDE",&DISABLE_FEED_OVERRIDE); @@ -138,7 +142,7 @@ BOOST_PYTHON_MODULE(emccanon) { def("GET_EXTERNAL_MIST",&GET_EXTERNAL_MIST); def("GET_EXTERNAL_MOTION_CONTROL_MODE",&GET_EXTERNAL_MOTION_CONTROL_MODE); def("GET_EXTERNAL_MOTION_CONTROL_TOLERANCE",&GET_EXTERNAL_MOTION_CONTROL_TOLERANCE); - def("SET_PARAMETER_FILE_NAME",&SET_PARAMETER_FILE_NAME); + def("SET_PARAMETER_FILE_NAME",&wrap_text); def("GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE", &GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE); def("GET_EXTERNAL_PARAMETER_FILE_NAME",&GET_EXTERNAL_PARAMETER_FILE_NAME); @@ -188,11 +192,11 @@ BOOST_PYTHON_MODULE(emccanon) { def("INIT_CANON",&INIT_CANON); def("LOCK_ROTARY",&LOCK_ROTARY); // def("LOCK_SPINDLE_Z",&LOCK_SPINDLE_Z); - def("LOGAPPEND",&LOGAPPEND); + def("LOGAPPEND",&wrap_text); def("LOGCLOSE",&LOGCLOSE); - def("LOG",&LOG); - def("LOGOPEN",&LOGOPEN); - def("MESSAGE",&MESSAGE); + def("LOG",&wrap_text); + def("LOGOPEN",&wrap_text); + def("MESSAGE",&wrap_text); def("MIST_OFF",&MIST_OFF); def("MIST_ON",&MIST_ON); // def("NURB_CONTROL_POINT",&NURB_CONTROL_POINT); diff --git a/src/emc/rs274ngc/gcode_renderer.cc b/src/emc/rs274ngc/gcode_renderer.cc index 801a4364ba7..816ad639011 100644 --- a/src/emc/rs274ngc/gcode_renderer.cc +++ b/src/emc/rs274ngc/gcode_renderer.cc @@ -867,16 +867,15 @@ GCodeRenderer::~GCodeRenderer() { // fill depends on, counted as a depth so nested spans close in order. The rest // of the vocabulary - `stop`, `notify`, the foam Z levels - is the canon's own // and reached it through the forward that precedes this call. -void GCodeRenderer::comment(const char *text) { - const char *rest; - if(!strncmp(text, "AXIS,", 5)) rest = text + 5; - else if(!strncmp(text, "PREVIEW,", 8)) rest = text + 8; +void GCodeRenderer::comment(std::string_view text) { + std::string_view rest; + if(text.starts_with("AXIS,")) rest = text.substr(5); + else if(text.starts_with("PREVIEW,")) rest = text.substr(8); else return; // The word up to the next comma, as `arg.split(",")[1]` took it. - size_t n = strcspn(rest, ","); - if(n != 4) return; - if(!strncmp(rest, "hide", 4)) suppress_ ++; - else if(!strncmp(rest, "show", 4)) suppress_ --; + rest = rest.substr(0, rest.find(',')); + if(rest == "hide") suppress_ ++; + else if(rest == "show") suppress_ --; } void GCodeRenderer::publish_line() { diff --git a/src/emc/rs274ngc/gcode_renderer.hh b/src/emc/rs274ngc/gcode_renderer.hh index e5131e80cb1..01aa92d7887 100644 --- a/src/emc/rs274ngc/gcode_renderer.hh +++ b/src/emc/rs274ngc/gcode_renderer.hh @@ -436,7 +436,7 @@ public: } // A comment, after the canon has had it: the `(AXIS,hide)`/`(AXIS,show)` // depth is the renderer's own. - void comment(const char *text) override; + void comment(std::string_view text) override; bool transform_points(const Point9 *in, size_t n, double *machine, double *display) const override; diff --git a/src/emc/rs274ngc/gcodemodule.cc b/src/emc/rs274ngc/gcodemodule.cc index f86b53c0a20..1f91631ef1f 100644 --- a/src/emc/rs274ngc/gcodemodule.cc +++ b/src/emc/rs274ngc/gcodemodule.cc @@ -62,7 +62,6 @@ int _task = 0; // control preview behaviour when remapping -char _parameter_file_name[LINELEN]; extern "C" PyObject* PyInit_interpreter(void); extern "C" PyObject* PyInit_emccanon(void); @@ -610,17 +609,17 @@ void DWELL(double time) { parse_state.canon->dwell(time); } -void MESSAGE(char *comment) { +void MESSAGE(std::string_view comment) { maybe_new_line(); forward("message", comment); } -void LOG(char * /*s*/) {} -void LOGOPEN(char * /*f*/) {} -void LOGAPPEND(char * /*f*/) {} +void LOG(std::string_view /*s*/) {} +void LOGOPEN(std::string_view /*f*/) {} +void LOGAPPEND(std::string_view /*f*/) {} void LOGCLOSE() {} -void COMMENT(const char *comment) { +void COMMENT(std::string_view comment) { maybe_new_line(); forward("comment", comment); if(parse_state.interp_error) return; @@ -685,7 +684,7 @@ extern bool GET_BLOCK_DELETE(void) { return bd; } -void CANON_ERROR(const char * /*fmt*/, ...) {}; +void CANON_ERROR(std::string_view /*message*/) {} void CLAMP_AXIS(CANON_AXIS /*axis*/) {} bool GET_OPTIONAL_PROGRAM_STOP() { return false;} void SET_OPTIONAL_PROGRAM_STOP(bool /*state*/) {} @@ -752,23 +751,18 @@ CANON_POSITION GET_EXTERNAL_POSITION() { CANON_POSITION GET_EXTERNAL_PROBE_POSITION() { return GET_EXTERNAL_POSITION(); } void INIT_CANON() {} -void SET_PARAMETER_FILE_NAME(const char *name) -{ - strncpy(_parameter_file_name, name, PARAMETER_FILE_NAME_LENGTH); -} +void SET_PARAMETER_FILE_NAME(std::string_view /*name*/) {} -void GET_EXTERNAL_PARAMETER_FILE_NAME(char *name, int max_size) { - name[0] = 0; +std::string GET_EXTERNAL_PARAMETER_FILE_NAME() { // Not on the interp_error protocol: a canon without the attribute has // always just left the name empty, with the error still on the indicator. try { - std::string file = py::cast( + return py::cast( py::handle(parse_state.callback).attr("parameter_file")); - memset(name, 0, max_size); - strncpy(name, file.c_str(), max_size - 1); } catch(py::error_already_set &e) { e.restore(); } + return {}; } CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() { return CANON_UNITS_INCHES; } CANON_TOOL_TABLE GET_EXTERNAL_TOOL_TABLE(int pocket) { diff --git a/src/emc/rs274ngc/gcodemodule.hh b/src/emc/rs274ngc/gcodemodule.hh index db21a558a0a..3dd53e3c39a 100644 --- a/src/emc/rs274ngc/gcodemodule.hh +++ b/src/emc/rs274ngc/gcodemodule.hh @@ -200,7 +200,7 @@ public: // `(AXIS,stop)` still stops the parse before the word after it could open // a hidden span. Only the renderer listens, for its own `(AXIS,hide)` // depth. - virtual void comment(const char * /*text*/) {} + virtual void comment(std::string_view /*text*/) {} // `n` program points where the transform as it stands puts them: the // same machine and display coordinates a move endpoint on this line diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 780890fddec..99efe2bd24d 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -754,6 +754,13 @@ int Interp::find_remappings(block_pointer block, setup_pointer settings) /***********************************************************************/ +// The parameter file the canon names, or the default when it names none. +static std::string parameter_file() +{ + std::string name = GET_EXTERNAL_PARAMETER_FILE_NAME(); + return name.empty() ? RS274NGC_PARAMETER_FILE_NAME_DEFAULT : name; +} + /*! Interp::exit Returned Value: int (INTERP_OK) @@ -771,13 +778,7 @@ written. Otherwise, the default parameter file name is used. int Interp::exit() { - char file_name[LINELEN]; - - GET_EXTERNAL_PARAMETER_FILE_NAME(file_name, (LINELEN - 1)); - save_parameters(((file_name[0] == - 0) ? - RS274NGC_PARAMETER_FILE_NAME_DEFAULT : - file_name), _setup.parameters); + save_parameters(parameter_file().c_str(), _setup.parameters); reset(); // interpreter shutdown Python hook @@ -833,7 +834,6 @@ always calls SET_FEED_REFERENCE(CANON_XYZ). int Interp::init() { int k; // starting index in parameters of origin offsets - char filename[LINELEN]; double *pars; // short name for _setup.parameters char *iniFileName; @@ -1108,10 +1108,7 @@ int Interp::init() } USE_LENGTH_UNITS(_setup.length_units); - GET_EXTERNAL_PARAMETER_FILE_NAME(filename, LINELEN); - if (filename[0] == 0) - rs274ngc_strxcpy(filename, RS274NGC_PARAMETER_FILE_NAME_DEFAULT); - CHP(restore_parameters(filename)); + CHP(restore_parameters(parameter_file().c_str())); pars = _setup.parameters; _setup.origin_index = (int) (pars[5220] + 0.0001); if(_setup.origin_index < 1 || _setup.origin_index > 9) { @@ -2109,7 +2106,6 @@ the controller. int Interp::synch() { - char file_name[LINELEN]; const CANON_POSITION pos = GET_EXTERNAL_POSITION(); _setup.current_x = pos.x; _setup.current_y = pos.y; @@ -2149,11 +2145,7 @@ int Interp::synch() _setup.spindle_mode[s] = SPINDLE_MODE::CONSTANT_RPM; _setup.css_maximum[s] = 0.0; } - GET_EXTERNAL_PARAMETER_FILE_NAME(file_name, (LINELEN - 1)); - save_parameters(((file_name[0] == - 0) ? - RS274NGC_PARAMETER_FILE_NAME_DEFAULT : - file_name), _setup.parameters); + save_parameters(parameter_file().c_str(), _setup.parameters); load_tool_table(); /* must set _setup.tool_max first */ @@ -2594,23 +2586,17 @@ int Interp::ini_load(const char *filename) logDebug("Opened inifile:%s:", filename); - char parameter_file_name[LINELEN]={}; + std::string parameter_file_name; if (auto inistring = inifile.findString("PARAMETER_FILE", "RS274NGC")) { - if (inistring->length() >= sizeof(parameter_file_name)) { - logDebug("%s:[RS274NGC]PARAMETER_FILE is too long (max len %zu)", - filename, sizeof(parameter_file_name)-1); - } else { - strncpy(parameter_file_name, inistring->c_str(), sizeof(parameter_file_name)-1); - parameter_file_name[sizeof(parameter_file_name)-1] = '\0'; - logDebug("found PARAMETER_FILE:%s:", parameter_file_name); - } + parameter_file_name = *inistring; + logDebug("found PARAMETER_FILE:%s:", inistring->c_str()); } else { // not found, leave RS274NGC_PARAMETER_FILE alone logDebug("did not find PARAMETER_FILE"); } SET_PARAMETER_FILE_NAME(parameter_file_name); - CHKS((strlen(parameter_file_name) == 0), _("Parameter file name is missing")); + CHKS(parameter_file_name.empty(), _("Parameter file name is missing")); return 0; } diff --git a/src/emc/sai/driver.cc b/src/emc/sai/driver.cc index 850471d1a35..e0b9661f43e 100644 --- a/src/emc/sai/driver.cc +++ b/src/emc/sai/driver.cc @@ -30,6 +30,8 @@ #include /* toupper */ #include #include +#include +#include #include #include @@ -360,21 +362,16 @@ Called By: main */ -int designate_parameter_file(char * parameter_file_name) +int designate_parameter_file(std::string ¶meter_file_name) { - FILE * test_port; - - fprintf(stderr, "name of parameter file => "); - if(!fgets(parameter_file_name, PARAMETER_FILE_NAME_LENGTH, stdin)) + std::cerr << "name of parameter file => "; + if (!std::getline(std::cin, parameter_file_name)) return 1; - parameter_file_name[strlen(parameter_file_name) - 1] = 0; - test_port = fopen(parameter_file_name, "r"); - if (test_port == NULL) + if (!std::ifstream(parameter_file_name)) { - fprintf(stderr, "Cannot open %s\n", parameter_file_name); + std::cerr << "Cannot open " << parameter_file_name << "\n"; return 1; } - fclose(test_port); return 0; } diff --git a/src/emc/sai/saicanon.cc b/src/emc/sai/saicanon.cc index 0a03e45c49b..2dd205651e5 100644 --- a/src/emc/sai/saicanon.cc +++ b/src/emc/sai/saicanon.cc @@ -46,7 +46,7 @@ StandaloneInterpInternals _sai = StandaloneInterpInternals(); -char _parameter_file_name[PARAMETER_FILE_NAME_LENGTH]; +std::string _parameter_file_name; /* where to print */ FILE * _outfile = stdout; /* where to print, set in main */ @@ -609,8 +609,8 @@ void CLAMP_AXIS(CANON_AXIS axis) (axis == CANON_AXIS_B) ? "CANON_AXIS_B" : (axis == CANON_AXIS_C) ? "CANON_AXIS_C" : "UNKNOWN");} -void COMMENT(const char *s) -{PRINT("COMMENT(\"%s\")\n", s);} +void COMMENT(std::string_view s) +{PRINT("COMMENT(\"%.*s\")\n", (int)s.size(), s.data());} void DISABLE_ADAPTIVE_FEED() {PRINT("DISABLE_ADAPTIVE_FEED()\n");} @@ -655,15 +655,15 @@ void INIT_CANON() { } -void MESSAGE(char *s) -{PRINT("MESSAGE(\"%s\")\n", s);} +void MESSAGE(std::string_view s) +{PRINT("MESSAGE(\"%.*s\")\n", (int)s.size(), s.data());} -void LOG(char *s) -{PRINT("LOG(\"%s\")\n", s);} -void LOGOPEN(char *s) -{PRINT("LOGOPEN(\"%s\")\n", s);} -void LOGAPPEND(char *s) -{PRINT("LOGAPPEND(\"%s\")\n", s);} +void LOG(std::string_view s) +{PRINT("LOG(\"%.*s\")\n", (int)s.size(), s.data());} +void LOGOPEN(std::string_view s) +{PRINT("LOGOPEN(\"%.*s\")\n", (int)s.size(), s.data());} +void LOGAPPEND(std::string_view s) +{PRINT("LOGAPPEND(\"%.*s\")\n", (int)s.size(), s.data());} void LOGCLOSE() {PRINT("LOGCLOSE()\n");} @@ -816,26 +816,14 @@ extern int GET_EXTERNAL_G68_FRAME(double * /*origin*/, double * /*rotation*/) return -1; } -extern void SET_PARAMETER_FILE_NAME(const char *name) +void SET_PARAMETER_FILE_NAME(std::string_view name) { - strncpy(_parameter_file_name, name, PARAMETER_FILE_NAME_LENGTH - 1); + _parameter_file_name = name; } -void GET_EXTERNAL_PARAMETER_FILE_NAME( - char * file_name, /* string: to copy file name into */ - int max_size) /* maximum number of characters to copy */ +std::string GET_EXTERNAL_PARAMETER_FILE_NAME() { - // Paranoid checks - if (NULL == file_name) - return; - - if (max_size < 0) - return; - - if (strlen(_parameter_file_name) < (size_t)max_size) - strcpy(file_name, _parameter_file_name); - else - file_name[0] = 0; + return _parameter_file_name; } CANON_PLANE GET_EXTERNAL_PLANE() @@ -1034,26 +1022,9 @@ EmcPose GET_EXTERNAL_OFFSETS() { }; /* Sends error message */ -void CANON_ERROR(const char *fmt, ...) -{ - va_list ap; - - if (fmt != NULL) { - va_start(ap, fmt); - char *err; - int res = vasprintf(&err, fmt, ap); - if(res < 0) err = NULL; - va_end(ap); - if(err) - { - PRINT("CANON_ERROR(%s)\n", err); - free(err); - } - else - { - PRINT("CANON_ERROR(vasprintf failed: %s)\n", strerror(errno)); - } - } +void CANON_ERROR(std::string_view message) +{ + PRINT("CANON_ERROR(%.*s)\n", (int)message.size(), message.data()); } void reset_internals() diff --git a/src/emc/sai/saicanon.hh b/src/emc/sai/saicanon.hh index f5946ef5039..56981652405 100644 --- a/src/emc/sai/saicanon.hh +++ b/src/emc/sai/saicanon.hh @@ -12,7 +12,7 @@ class InterpBase; extern StandaloneInterpInternals _sai; extern InterpBase *pinterp; extern FILE *_outfile; -extern char _parameter_file_name[PARAMETER_FILE_NAME_LENGTH]; +extern std::string _parameter_file_name; struct StandaloneInterpInternals { diff --git a/src/emc/task/emccanon.cc b/src/emc/task/emccanon.cc index 1d977d0e12f..37d8a418d39 100644 --- a/src/emc/task/emccanon.cc +++ b/src/emc/task/emccanon.cc @@ -53,6 +53,9 @@ #include #include #include // strncpy() +#include +#include +#include #include // isspace() #include "nml_intf/emc.hh" // EMC NML #include "nml_intf/emc_nml.hh" @@ -3146,59 +3149,25 @@ void CLAMP_AXIS(CANON_AXIS /*axis*/) /*! \todo FIXME-- unimplemented */ } -/* - setString and addString initializes or adds src to dst, never exceeding - dst's maxlen chars. -*/ - -static char *setString(char *dst, const char *src, int maxlen) -{ - dst[0] = 0; - strncat(dst, src, maxlen - 1); - dst[maxlen - 1] = 0; - return dst; -} - -static char *addString(char *dst, const char *src, int maxlen) -{ - int dstlen = strlen(dst); - int srclen = strlen(src); - int actlen; - - if (srclen >= maxlen - dstlen) { - actlen = maxlen - dstlen - 1; - dst[maxlen - 1] = 0; - } else { - actlen = srclen; - } - - strncat(dst, src, actlen); - - return dst; -} - /* The probe file is opened with a hot-comment (PROBEOPEN ), and the results of each probed point are written to that file. The file is closed with a (PROBECLOSE) comment. */ -static FILE *probefile = NULL; +static std::ofstream probefile; -void COMMENT(const char *comment) +void COMMENT(std::string_view comment) { // nothing need be done here, but you can play tricks with hot comments - char msg[LINELEN]; - char probefilename[LINELEN]; - const char *ptr; - // set RPY orientation for subsequent moves - if (!strncmp(comment, "RPY", strlen("RPY"))) { + if (comment.starts_with("RPY")) { PM_RPY rpy; // it's RPY

- if (3 != - sscanf(comment, "%*s %lf %lf %lf", &rpy.r, &rpy.p, &rpy.y)) { + std::istringstream args{std::string(comment)}; + std::string word; + if (!(args >> word >> rpy.r >> rpy.p >> rpy.y)) { // print current orientation printf("rpy = %f %f %f, quat = %f %f %f %f\n", rpy.r, rpy.p, rpy.y, quat.s, quat.x, quat.y, quat.z); @@ -3211,29 +3180,23 @@ void COMMENT(const char *comment) return; } // open probe output file - if (!strncmp(comment, "PROBEOPEN", strlen("PROBEOPEN"))) { - // position ptr to first char after PROBEOPEN - ptr = &comment[strlen("PROBEOPEN")]; - // and step over white space to name, or NULL - while (isspace(*ptr)) { - ptr++; + if (comment.starts_with("PROBEOPEN")) { + // the name follows PROBEOPEN after white space + std::string_view name = comment.substr(strlen("PROBEOPEN")); + while (!name.empty() && isspace((unsigned char)name.front())) { + name.remove_prefix(1); } - setString(probefilename, ptr, LINELEN); - if (NULL == (probefile = fopen(probefilename, "wt"))) { + probefile.close(); + probefile.open(std::string(name)); + if (!probefile) { // pop up a warning message - setString(msg, "can't open probe file ", LINELEN); - addString(msg, probefilename, LINELEN); - MESSAGE(msg); - probefile = NULL; + MESSAGE("can't open probe file " + std::string(name)); } return; } // close probe output file - if (!strncmp(comment, "PROBECLOSE", strlen("PROBECLOSE"))) { - if (probefile != NULL) { - fclose(probefile); - probefile = NULL; - } + if (comment.starts_with("PROBECLOSE")) { + probefile.close(); return; } @@ -3343,41 +3306,43 @@ void FLOOD_ON() SIMPLE_COMMAND_(); } -void MESSAGE(char *s) +void MESSAGE(std::string_view s) { auto operator_display_msg = std::make_unique(); flush_segments(); - strncpy(operator_display_msg->display, s, LINELEN-1); - operator_display_msg->display[LINELEN - 1] = 0; + const size_t n = std::min(s.size(), size_t(LINELEN - 1)); + s.copy(operator_display_msg->display, n); + operator_display_msg->display[n] = 0; interp_list.append(std::move(operator_display_msg)); } -static FILE *logfile = NULL; +static std::ofstream logfile; -void LOG(char *s) { +void LOG(std::string_view s) { flush_segments(); - if(logfile) { fprintf(logfile, "%s\n", s); fflush(logfile); } - fprintf(stderr, "LOG(%s)\n", s); + if(logfile.is_open()) logfile << s << std::endl; + std::cerr << "LOG(" << s << ")\n"; +} +static void log_open(std::string_view name, std::ios::openmode mode, const char *what) { + logfile.close(); + logfile.clear(); + logfile.open(std::string(name), mode); + std::cerr << what << "(" << name << ") -> " << (logfile ? "open" : "failed") << "\n"; } -void LOGOPEN(char *name) { - if(logfile) fclose(logfile); - logfile = fopen(name, "wt"); - fprintf(stderr, "LOGOPEN(%s) -> %p\n", name, logfile); +void LOGOPEN(std::string_view name) { + log_open(name, std::ios::out | std::ios::trunc, "LOGOPEN"); } -void LOGAPPEND(char *name) { - if(logfile) fclose(logfile); - logfile = fopen(name, "at"); - fprintf(stderr, "LOGAPPEND(%s) -> %p\n", name, logfile); +void LOGAPPEND(std::string_view name) { + log_open(name, std::ios::out | std::ios::app, "LOGAPPEND"); } void LOGCLOSE() { - if(logfile) fclose(logfile); - logfile = NULL; + logfile.close(); fprintf(stderr, "LOGCLOSE()\n"); } @@ -3550,20 +3515,15 @@ void INIT_CANON() } /* Sends error message */ -void CANON_ERROR(const char *fmt, ...) +void CANON_ERROR(std::string_view message) { - va_list ap; auto operator_error_msg = std::make_unique(); flush_segments(); - if (fmt != NULL) { - va_start(ap, fmt); - vsnprintf(operator_error_msg->error, sizeof(operator_error_msg->error), fmt, ap); - va_end(ap); - } else { - operator_error_msg->error[0] = 0; - } + const size_t n = std::min(message.size(), sizeof(operator_error_msg->error) - 1); + message.copy(operator_error_msg->error, n); + operator_error_msg->error[n] = 0; interp_list.append(std::move(operator_error_msg)); } @@ -3658,9 +3618,9 @@ CANON_POSITION GET_EXTERNAL_PROBE_POSITION() position = unoffset_and_unrotate_pos(pos); to_prog(position); - if (probefile != NULL) { + if (probefile.is_open()) { if (last_probed_position != position) { - fprintf(probefile, "%f %f %f %f %f %f %f %f %f\n", + probefile << fmt::format("{:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f} {:f}\n", position.x, position.y, position.z, position.a, position.b, position.c, position.u, position.v, position.w); @@ -3802,28 +3762,16 @@ CANON_DIRECTION GET_EXTERNAL_SPINDLE(int spindle) return CANON_COUNTERCLOCKWISE; } -static char _parameter_file_name[LINELEN]; +static std::string _parameter_file_name; -void SET_PARAMETER_FILE_NAME(const char *name) +void SET_PARAMETER_FILE_NAME(std::string_view name) { - strncpy(_parameter_file_name, name, PARAMETER_FILE_NAME_LENGTH); + _parameter_file_name = name; } -void GET_EXTERNAL_PARAMETER_FILE_NAME(char *file_name, /* string: to copy - file name into */ - int max_size) -{ /* maximum number of characters to copy */ - // Paranoid checks - if (NULL == file_name) - return; - - if (max_size < 0) - return; - - if (strlen(_parameter_file_name) < ((size_t) max_size)) - strcpy(file_name, _parameter_file_name); - else - file_name[0] = 0; +std::string GET_EXTERNAL_PARAMETER_FILE_NAME() +{ + return _parameter_file_name; } CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() diff --git a/tests/interp/compile/use-rs274.cc b/tests/interp/compile/use-rs274.cc index fbcde627c3c..91e63e533f1 100644 --- a/tests/interp/compile/use-rs274.cc +++ b/tests/interp/compile/use-rs274.cc @@ -152,7 +152,7 @@ void SELECT_TOOL(int tool) {} void CHANGE_TOOL_NUMBER(int number) {} void RELOAD_TOOLDATA() {} void CLAMP_AXIS(CANON_AXIS axis) {} -void COMMENT(const char *s) { puts(s); } +void COMMENT(std::string_view s) { printf("%.*s\n", (int)s.size(), s.data()); } void DISABLE_ADAPTIVE_FEED() {} void ENABLE_ADAPTIVE_FEED() {} void DISABLE_FEED_OVERRIDE() {} @@ -163,10 +163,10 @@ void DISABLE_FEED_HOLD() {} void ENABLE_FEED_HOLD() {} void FLOOD_OFF() {} void FLOOD_ON() {} -void MESSAGE(char *s) {} -void LOG(char *s) {} -void LOGOPEN(char *s) {} -void LOGAPPEND(char *s) {} +void MESSAGE(std::string_view s) {} +void LOG(std::string_view s) {} +void LOGOPEN(std::string_view s) {} +void LOGAPPEND(std::string_view s) {} void LOGCLOSE() {} void MIST_OFF() {} void MIST_ON() {} @@ -206,11 +206,9 @@ int GET_EXTERNAL_MIST() { return 0; } CANON_MOTION_MODE GET_EXTERNAL_MOTION_CONTROL_MODE() { return CANON_EXACT_STOP; } double GET_EXTERNAL_MOTION_CONTROL_TOLERANCE() { return 0.0; } -extern void SET_PARAMETER_FILE_NAME(const char *name) {} +void SET_PARAMETER_FILE_NAME(std::string_view name) {} double GET_EXTERNAL_MOTION_CONTROL_NAIVECAM_TOLERANCE() { return 0.0; } -void GET_EXTERNAL_PARAMETER_FILE_NAME(char *filename, int max_size) { - snprintf(filename, max_size, "%s", "rs274ngc.var"); -} +std::string GET_EXTERNAL_PARAMETER_FILE_NAME() { return "rs274ngc.var"; } CANON_PLANE GET_EXTERNAL_PLANE() { return CANON_PLANE::XY; } CANON_POSITION GET_EXTERNAL_POSITION() { return CANON_POSITION(); } CANON_POSITION GET_EXTERNAL_PROBE_POSITION() { return CANON_POSITION(); } @@ -235,7 +233,7 @@ double GET_EXTERNAL_ANALOG_INPUT(int index, double def) { return 0.0; } int GET_EXTERNAL_AXIS_MASK() { return 7; } void FINISH(void) {} void ON_RESET(void) {} -void CANON_ERROR(const char *fmt, ...) {} +void CANON_ERROR(std::string_view message) {} void UPDATE_TAG(const StateTag& tag) {} USER_DEFINED_FUNCTION_TYPE USER_DEFINED_FUNCTION[USER_DEFINED_FUNCTION_NUM]; From 51febee35ec5623cb0f37f810a35e97b989212f7 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 02:09:44 +0100 Subject: [PATCH 04/19] sai: print canon calls with {fmt} PRINT and ECHO_WITH_ARGS wrapped fprintf, so a std::string_view had to be passed as "%.*s" with a separate length and pointer. They now use fmt::print, which takes a string_view as one argument and checks every format string at compile time. The UNEXPECTED diagnostics move to fmt::print as well. The output is unchanged: rs274 -g prints the same bytes as before for all 651 .ngc files under tests/ and nc_files/. START_SPEED_FEED_SYNC prints its bool with {:d}, because {} would print "true" where %d printed "1". rs274 already links -lfmt. --- src/emc/sai/saicanon.cc | 147 ++++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 73 deletions(-) diff --git a/src/emc/sai/saicanon.cc b/src/emc/sai/saicanon.cc index 2dd205651e5..b71da2657c8 100644 --- a/src/emc/sai/saicanon.cc +++ b/src/emc/sai/saicanon.cc @@ -41,8 +41,9 @@ #include #include #include +#include -#define UNEXPECTED_MSG fprintf(stderr,"UNEXPECTED %s %d\n",__FILE__,__LINE__); +#define UNEXPECTED_MSG fmt::print(stderr, "UNEXPECTED {} {}\n", __FILE__, __LINE__); StandaloneInterpInternals _sai = StandaloneInterpInternals(); @@ -97,28 +98,28 @@ void print_nc_line_number() fprintf(_outfile, "N..... "); } -#define ECHO_WITH_ARGS(fmt, ...) PRINT("%s(" fmt ")\n", __FUNCTION__, ##__VA_ARGS__) +#define ECHO_WITH_ARGS(fmt, ...) PRINT("{}(" fmt ")\n", __FUNCTION__, ##__VA_ARGS__) #define PRINT(control, ...) do \ { \ - fprintf(_outfile, "%5d ", _sai._line_number++); \ + fmt::print(_outfile, "{:5d} ", _sai._line_number++); \ print_nc_line_number(); \ - fprintf(_outfile, control, ##__VA_ARGS__); \ + fmt::print(_outfile, control, ##__VA_ARGS__); \ } while (false) /* Representation */ void SET_XY_ROTATION(double t) { - ECHO_WITH_ARGS("%.4f", t); + ECHO_WITH_ARGS("{:.4f}", t); } void HOME_CYCLE(void) { ECHO_WITH_ARGS(""); } -void HOME_CYCLE_JOINT(int joint) { ECHO_WITH_ARGS("%d", joint); } +void HOME_CYCLE_JOINT(int joint) { ECHO_WITH_ARGS("{}", joint); } void SET_G68_FRAME(double x, double y, double z, const double rotation[9], int active) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f, " - "[%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f], %d", + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}, " + "[{:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}], {}", x, y, z, rotation[0], rotation[1], rotation[2], rotation[3], rotation[4], rotation[5], @@ -130,7 +131,7 @@ void SET_G5X_OFFSET(int index, double a, double b, double c, double /*u*/, double /*v*/, double /*w*/) { - ECHO_WITH_ARGS("%d, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f", + ECHO_WITH_ARGS("{}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}", index, x, y, z, a, b, c); _sai._program_position_x = _sai._program_position_x + _sai._g5x_x - x; _sai._program_position_y = _sai._program_position_y + _sai._g5x_y - y; @@ -150,7 +151,7 @@ void SET_G5X_OFFSET(int index, void SET_G92_OFFSET(double x, double y, double z, double a, double b, double c, double /*u*/, double /*v*/, double /*w*/) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f, %.4f, %.4f, %.4f", + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}, {:.4f}", x, y, z, a, b, c); _sai._program_position_x = _sai._program_position_x + _sai._g92_x - x; _sai._program_position_y = _sai._program_position_y + _sai._g92_y - y; @@ -218,7 +219,7 @@ void USE_LENGTH_UNITS(CANON_UNITS in_unit) /* Free Space Motion */ void SET_TRAVERSE_RATE(double rate) { - PRINT("SET_TRAVERSE_RATE(%.4f)\n", rate); + PRINT("SET_TRAVERSE_RATE({:.4f})\n", rate); _sai._traverse_rate = rate; } @@ -230,10 +231,10 @@ void STRAIGHT_TRAVERSE( int /*line_number*/, , double /*u*/, double /*v*/, double /*w*/ ) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f" - ", %.4f" /*AA*/ - ", %.4f" /*BB*/ - ", %.4f" /*CC*/ + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}" + ", {:.4f}" /*AA*/ + ", {:.4f}" /*BB*/ + ", {:.4f}" /*CC*/ , x, y, z , a /*AA*/ , b /*BB*/ @@ -250,18 +251,18 @@ void STRAIGHT_TRAVERSE( int /*line_number*/, /* Machining Attributes */ void SET_FEED_MODE(int spindle, int mode) { - PRINT("SET_FEED_MODE(%d, %d)\n", spindle, mode); + PRINT("SET_FEED_MODE({}, {})\n", spindle, mode); _sai._feed_mode = mode; } void SET_FEED_RATE(double rate) { - PRINT("SET_FEED_RATE(%.4f)\n", rate); + PRINT("SET_FEED_RATE({:.4f})\n", rate); _sai._feed_rate = rate; } void SET_FEED_REFERENCE(CANON_FEED_REFERENCE reference) { - PRINT("SET_FEED_REFERENCE(%s)\n", + PRINT("SET_FEED_REFERENCE({})\n", (reference == CANON_WORKPIECE) ? "CANON_WORKPIECE" : "CANON_XYZ"); } @@ -270,7 +271,7 @@ extern void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance, { /* G64_R_PLANNER: standalone-interp echo of the optional planner mode */ if (planner_type >= 0 || scurve_peak_scale >= 0.0) - PRINT("SET_PLANNER_MODE(type=%d, peak_scale=%f)\n", planner_type, scurve_peak_scale); + PRINT("SET_PLANNER_MODE(type={}, peak_scale={:f})\n", planner_type, scurve_peak_scale); _sai.motion_tolerance = 0; if (mode == CANON_EXACT_STOP) { @@ -285,7 +286,7 @@ extern void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance, else if (mode == CANON_CONTINUOUS) { _sai.motion_tolerance = tolerance; - PRINT("SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, %f)\n", tolerance); + PRINT("SET_MOTION_CONTROL_MODE(CANON_CONTINUOUS, {:f})\n", tolerance); _sai._motion_mode = CANON_CONTINUOUS; } else @@ -295,12 +296,12 @@ extern void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance, extern void SET_NAIVECAM_TOLERANCE(double tolerance) { _sai.naivecam_tolerance = tolerance; - PRINT("SET_NAIVECAM_TOLERANCE(%.4f)\n", tolerance); + PRINT("SET_NAIVECAM_TOLERANCE({:.4f})\n", tolerance); } void SELECT_PLANE(CANON_PLANE in_plane) { - PRINT("SELECT_PLANE(CANON_PLANE_%s)\n", + PRINT("SELECT_PLANE(CANON_PLANE_{})\n", ((in_plane == CANON_PLANE::XY) ? "XY" : (in_plane == CANON_PLANE::YZ) ? "YZ" : (in_plane == CANON_PLANE::XZ) ? "XZ" : "UNKNOWN")); @@ -308,10 +309,10 @@ void SELECT_PLANE(CANON_PLANE in_plane) } void SET_CUTTER_RADIUS_COMPENSATION(double radius) -{PRINT("SET_CUTTER_RADIUS_COMPENSATION(%.4f)\n", radius);} +{PRINT("SET_CUTTER_RADIUS_COMPENSATION({:.4f})\n", radius);} void START_CUTTER_RADIUS_COMPENSATION(int side) -{PRINT("START_CUTTER_RADIUS_COMPENSATION(%s)\n", +{PRINT("START_CUTTER_RADIUS_COMPENSATION({})\n", (side == CANON_SIDE_LEFT) ? "LEFT" : (side == CANON_SIDE_RIGHT) ? "RIGHT" : "UNKNOWN"); } @@ -329,7 +330,7 @@ void STOP_SPEED_FEED_SYNCH() /* Machining Functions G_5_2 */ void NURBS_G5_FEED(int /*lineno*/, const std::vector& nurbs_control_points, unsigned int /*nurbs_order*/, CANON_PLANE /*plane*/) { - ECHO_WITH_ARGS("%lu, ...", (unsigned long)nurbs_control_points.size()); + ECHO_WITH_ARGS("{}, ...", nurbs_control_points.size()); _sai._program_position_x = nurbs_control_points[nurbs_control_points.size()-1].NURBS_X; _sai._program_position_y = nurbs_control_points[nurbs_control_points.size()-1].NURBS_Y; @@ -339,7 +340,7 @@ void NURBS_G5_FEED(int /*lineno*/, const std::vector& nurbs void NURBS_G6_FEED(int /*lineno*/, const std::vector& nurbs_control_points, unsigned int /*k*/, double /*feedrate*/, int /*l*/, CANON_PLANE /*plane*/) { //fprintf(_outfile, "%5d ", _line_number++); print_nc_line_number(); - fprintf(_outfile, "saicanon NURBS_G6_FEED_(%lu, ...)\n", (unsigned long)nurbs_control_points.size()); + fmt::print(_outfile, "saicanon NURBS_G6_FEED_({}, ...)\n", nurbs_control_points.size()); _sai._program_position_x = nurbs_control_points[nurbs_control_points.size()-1].NURBS_X; _sai._program_position_y = nurbs_control_points[nurbs_control_points.size()-1].NURBS_Y; @@ -354,10 +355,10 @@ void ARC_FEED(int /*line_number*/, , double /*u*/, double /*v*/, double /*w*/ ) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f, %.4f, %d, %.4f" - ", %.4f" /*AA*/ - ", %.4f" /*BB*/ - ", %.4f" /*CC*/ + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}, {:.4f}, {}, {:.4f}" + ", {:.4f}" /*AA*/ + ", {:.4f}" /*BB*/ + ", {:.4f}" /*CC*/ , first_end, second_end, first_axis, second_axis, rotation, axis_end_point , a /*AA*/ @@ -395,10 +396,10 @@ void STRAIGHT_FEED(int /*line_number*/, , double /*u*/, double /*v*/, double /*w*/ ) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f" - ", %.4f" /*AA*/ - ", %.4f" /*BB*/ - ", %.4f" /*CC*/ + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}" + ", {:.4f}" /*AA*/ + ", {:.4f}" /*BB*/ + ", {:.4f}" /*CC*/ , x, y, z , a /*AA*/ , b /*BB*/ @@ -434,10 +435,10 @@ void STRAIGHT_PROBE(int /*line_number*/, dz = (_sai._program_position_z - z); distance = sqrt((dx * dx) + (dy * dy) + (dz * dz)); - ECHO_WITH_ARGS("%.4f, %.4f, %.4f" - ", %.4f" /*AA*/ - ", %.4f" /*BB*/ - ", %.4f" /*CC*/ + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}" + ", {:.4f}" /*AA*/ + ", {:.4f}" /*BB*/ + ", {:.4f}" /*CC*/ , x, y, z , a /*AA*/ , b /*BB*/ @@ -464,13 +465,13 @@ void STRAIGHT_PROBE(int /*line_number*/, void RIGID_TAP(int /*line_number*/, double x, double y, double z, double /*scale*/) { - ECHO_WITH_ARGS("%.4f, %.4f, %.4f", x, y, z); + ECHO_WITH_ARGS("{:.4f}, {:.4f}, {:.4f}", x, y, z); } void DWELL(double seconds) { - ECHO_WITH_ARGS("%.4f", seconds); + ECHO_WITH_ARGS("{:.4f}", seconds); } /* Spindle Functions */ @@ -478,32 +479,32 @@ void SPINDLE_RETRACT_TRAVERSE() {PRINT("SPINDLE_RETRACT_TRAVERSE()\n");} void SET_SPINDLE_MODE(int spindle, double arg) { - PRINT("SET_SPINDLE_MODE(%d %.4f)\n", spindle, arg); + PRINT("SET_SPINDLE_MODE({} {:.4f})\n", spindle, arg); } void START_SPINDLE_CLOCKWISE(int spindle, int /*wait_for_atspeed*/) { - PRINT("START_SPINDLE_CLOCKWISE(%i)\n", spindle); + PRINT("START_SPINDLE_CLOCKWISE({})\n", spindle); _sai._spindle_turning[spindle] = ((_sai._spindle_speed[spindle] == 0) ? CANON_STOPPED : CANON_CLOCKWISE); } void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int /*wait_for_atspeed*/) { - PRINT("START_SPINDLE_COUNTERCLOCKWISE(%i)\n", spindle); + PRINT("START_SPINDLE_COUNTERCLOCKWISE({})\n", spindle); _sai._spindle_turning[spindle] = ((_sai._spindle_speed[spindle] == 0) ? CANON_STOPPED : CANON_COUNTERCLOCKWISE); } void SET_SPINDLE_SPEED(int spindle, double rpm) { - PRINT("SET_SPINDLE_SPEED(%i, %.4f)\n", spindle, rpm); + PRINT("SET_SPINDLE_SPEED({}, {:.4f})\n", spindle, rpm); _sai._spindle_speed[spindle] = rpm; } void STOP_SPINDLE_TURNING(int spindle, int /*wait_for_atspeed*/) { - PRINT("STOP_SPINDLE_TURNING(%i)\n", spindle); + PRINT("STOP_SPINDLE_TURNING({})\n", spindle); _sai._spindle_turning[spindle] = CANON_STOPPED; } @@ -511,12 +512,12 @@ void SPINDLE_RETRACT() {PRINT("SPINDLE_RETRACT()\n");} void ORIENT_SPINDLE(int spindle, double orientation, int mode) -{PRINT("ORIENT_SPINDLE(%d, %.4f, %d)\n", spindle, orientation, mode); +{PRINT("ORIENT_SPINDLE({}, {:.4f}, {})\n", spindle, orientation, mode); } void WAIT_SPINDLE_ORIENT_COMPLETE(int spindle, double timeout) { - PRINT("SPINDLE.%i.WAIT_ORIENT_COMPLETE(%.4f)\n", spindle, timeout); + PRINT("SPINDLE.{}.WAIT_ORIENT_COMPLETE({:.4f})\n", spindle, timeout); } void USE_NO_SPINDLE_FORCE() @@ -545,11 +546,11 @@ void SET_TOOL_TABLE_ENTRY(int idx, int toolno, const EmcPose& offset, double dia tdata.backangle = backangle; tdata.orientation = orientation; if (tooldata_put(tdata,idx) == IDX_FAIL) { - fprintf(stderr,"UNEXPECTED idx %s %d\n",__FILE__,__LINE__); + fmt::print(stderr, "UNEXPECTED idx {} {}\n", __FILE__, __LINE__); } #endif //} - ECHO_WITH_ARGS("%d, %d, %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f, %.4f, %.4f, %d", + ECHO_WITH_ARGS("{}, {}, {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f} {:.4f}, {:.4f}, {:.4f}, {}", idx, toolno, offset.tran.x, offset.tran.y, offset.tran.z, offset.a, offset.b, offset.c, offset.u, offset.v, offset.w, frontangle, backangle, orientation); @@ -558,7 +559,7 @@ void SET_TOOL_TABLE_ENTRY(int idx, int toolno, const EmcPose& offset, double dia void USE_TOOL_LENGTH_OFFSET(const EmcPose& offset) { _sai._tool_offset = offset; - ECHO_WITH_ARGS("%.4f %.4f %.4f, %.4f %.4f %.4f, %.4f %.4f %.4f", + ECHO_WITH_ARGS("{:.4f} {:.4f} {:.4f}, {:.4f} {:.4f} {:.4f}, {:.4f} {:.4f} {:.4f}", offset.tran.x, offset.tran.y, offset.tran.z, offset.a, offset.b, offset.c, offset.u, offset.v, offset.w); } @@ -575,20 +576,20 @@ void CHANGE_TOOL() } _sai._tools[0] = tdata; if (tooldata_put(tdata,0) == IDX_FAIL) { - fprintf(stderr,"UNEXPECTED idx %s %d\n",__FILE__,__LINE__); + fmt::print(stderr, "UNEXPECTED idx {} {}\n", __FILE__, __LINE__); } #endif //} } void SELECT_TOOL(int tool)//TODO: fix slot number { - PRINT("SELECT_TOOL(%d)\n", tool); + PRINT("SELECT_TOOL({})\n", tool); _sai._selected_tool = tool; } void CHANGE_TOOL_NUMBER(int tool) { - PRINT("CHANGE_TOOL_NUMBER(%d)\n", tool); + PRINT("CHANGE_TOOL_NUMBER({})\n", tool); _sai._active_slot = tool; } @@ -601,7 +602,7 @@ void RELOAD_TOOLDATA(void) /* Misc Functions */ void CLAMP_AXIS(CANON_AXIS axis) -{PRINT("CLAMP_AXIS(%s)\n", +{PRINT("CLAMP_AXIS({})\n", (axis == CANON_AXIS_X) ? "CANON_AXIS_X" : (axis == CANON_AXIS_Y) ? "CANON_AXIS_Y" : (axis == CANON_AXIS_Z) ? "CANON_AXIS_Z" : @@ -610,7 +611,7 @@ void CLAMP_AXIS(CANON_AXIS axis) (axis == CANON_AXIS_C) ? "CANON_AXIS_C" : "UNKNOWN");} void COMMENT(std::string_view s) -{PRINT("COMMENT(\"%.*s\")\n", (int)s.size(), s.data());} +{PRINT("COMMENT(\"{}\")\n", s);} void DISABLE_ADAPTIVE_FEED() {PRINT("DISABLE_ADAPTIVE_FEED()\n");} @@ -622,7 +623,7 @@ void DISABLE_FEED_OVERRIDE() {PRINT("DISABLE_FEED_OVERRIDE()\n"); fo_enable = false; } void DISABLE_SPEED_OVERRIDE(int spindle) -{PRINT("DISABLE_SPEED_OVERRIDE(%i)\n", spindle); so_enable = false; } +{PRINT("DISABLE_SPEED_OVERRIDE({})\n", spindle); so_enable = false; } void ENABLE_ADAPTIVE_FEED() {PRINT("ENABLE_ADAPTIVE_FEED()\n");} @@ -634,10 +635,10 @@ void ENABLE_FEED_OVERRIDE() {PRINT("ENABLE_FEED_OVERRIDE()\n"); fo_enable = true; } void ENABLE_SPEED_OVERRIDE(int spindle) -{PRINT("ENABLE_SPEED_OVERRIDE(%i)\n", spindle); so_enable = true; } +{PRINT("ENABLE_SPEED_OVERRIDE({})\n", spindle); so_enable = true; } void LOCK_SPEED_OVERRIDE(int spindle) -{PRINT("LOCK_SPEED_OVERRIDE(%i)\n", spindle); } +{PRINT("LOCK_SPEED_OVERRIDE({})\n", spindle); } void FLOOD_OFF() { @@ -656,14 +657,14 @@ void INIT_CANON() } void MESSAGE(std::string_view s) -{PRINT("MESSAGE(\"%.*s\")\n", (int)s.size(), s.data());} +{PRINT("MESSAGE(\"{}\")\n", s);} void LOG(std::string_view s) -{PRINT("LOG(\"%.*s\")\n", (int)s.size(), s.data());} +{PRINT("LOG(\"{}\")\n", s);} void LOGOPEN(std::string_view s) -{PRINT("LOGOPEN(\"%.*s\")\n", (int)s.size(), s.data());} +{PRINT("LOGOPEN(\"{}\")\n", s);} void LOGAPPEND(std::string_view s) -{PRINT("LOGAPPEND(\"%.*s\")\n", (int)s.size(), s.data());} +{PRINT("LOGAPPEND(\"{}\")\n", s);} void LOGCLOSE() {PRINT("LOGCLOSE()\n");} @@ -689,7 +690,7 @@ void TURN_PROBE_ON() {PRINT("TURN_PROBE_ON()\n");} void UNCLAMP_AXIS(CANON_AXIS axis) -{PRINT("UNCLAMP_AXIS(%s)\n", +{PRINT("UNCLAMP_AXIS({})\n", (axis == CANON_AXIS_X) ? "CANON_AXIS_X" : (axis == CANON_AXIS_Y) ? "CANON_AXIS_Y" : (axis == CANON_AXIS_Z) ? "CANON_AXIS_Z" : @@ -758,9 +759,9 @@ void START_SPEED_FEED_SYNCH(int /*spindle*/, double sync, bool vel, double angle // only print the angle when one was asked for, so that the expected output // of tests predating the D word stays valid if (angle_degrees != 0.0) - PRINT("START_SPEED_FEED_SYNC(%f,%d,%f)\n", sync, vel, angle_degrees); + PRINT("START_SPEED_FEED_SYNC({:f},{:d},{:f})\n", sync, vel, angle_degrees); else - PRINT("START_SPEED_FEED_SYNC(%f,%d)\n", sync, vel); + PRINT("START_SPEED_FEED_SYNC({:f},{:d})\n", sync, vel); } CANON_MOTION_MODE motion_mode; @@ -945,37 +946,37 @@ int USER_DEFINED_FUNCTION_ADD(USER_DEFINED_FUNCTION_TYPE func, int num) void SET_MOTION_OUTPUT_BIT(int index) { - PRINT("SET_MOTION_OUTPUT_BIT(%d)\n", index); + PRINT("SET_MOTION_OUTPUT_BIT({})\n", index); return; } void CLEAR_MOTION_OUTPUT_BIT(int index) { - PRINT("CLEAR_MOTION_OUTPUT_BIT(%d)\n", index); + PRINT("CLEAR_MOTION_OUTPUT_BIT({})\n", index); return; } void SET_MOTION_OUTPUT_VALUE(int index, double value) { - PRINT("SET_MOTION_OUTPUT_VALUE(%d,%f)\n", index, value); + PRINT("SET_MOTION_OUTPUT_VALUE({},{:f})\n", index, value); return; } void SET_AUX_OUTPUT_BIT(int index) { - PRINT("SET_AUX_OUTPUT_BIT(%d)\n", index); + PRINT("SET_AUX_OUTPUT_BIT({})\n", index); return; } void CLEAR_AUX_OUTPUT_BIT(int index) { - PRINT("CLEAR_AUX_OUTPUT_BIT(%d)\n", index); + PRINT("CLEAR_AUX_OUTPUT_BIT({})\n", index); return; } void SET_AUX_OUTPUT_VALUE(int index, double value) { - PRINT("SET_AUX_OUTPUT_VALUE(%d,%f)\n", index, value); + PRINT("SET_AUX_OUTPUT_VALUE({},{:f})\n", index, value); return; } @@ -1024,7 +1025,7 @@ EmcPose GET_EXTERNAL_OFFSETS() { /* Sends error message */ void CANON_ERROR(std::string_view message) { - PRINT("CANON_ERROR(%.*s)\n", (int)message.size(), message.data()); + PRINT("CANON_ERROR({})\n", message); } void reset_internals() @@ -1094,5 +1095,5 @@ void UPDATE_TAG(const StateTag& /*tag*/){ void SELECT_KINS_TYPE(int switchkins_type) { - ECHO_WITH_ARGS("%d", switchkins_type); + ECHO_WITH_ARGS("{}", switchkins_type); } From 260aa2859f023c7e3ac02e4d1fd01e555f64f18a Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 02:22:01 +0100 Subject: [PATCH 05/19] interp: remove the unused setup.log_file The interpreter logs through Interp::log_file, a FILE*. The char array of the same name in the setup struct was never read or written. --- src/emc/rs274ngc/interp_internal.hh | 1 - src/emc/rs274ngc/interp_setup.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/emc/rs274ngc/interp_internal.hh b/src/emc/rs274ngc/interp_internal.hh index ebe9d0d0d49..1ee2e326729 100644 --- a/src/emc/rs274ngc/interp_internal.hh +++ b/src/emc/rs274ngc/interp_internal.hh @@ -863,7 +863,6 @@ struct setup bool feed_hold; // feed hold is enabled int loggingLevel; // 0 means logging is off int debugmask; // from INI EMC/DEBUG - char log_file[PATH_MAX]; char program_prefix[PATH_MAX]; // program directory const char *subroutines[MAX_SUB_DIRS]; // subroutines directories int use_lazy_close; // wait until next open before closing diff --git a/src/emc/rs274ngc/interp_setup.cc b/src/emc/rs274ngc/interp_setup.cc index bd513053de2..23fed349478 100644 --- a/src/emc/rs274ngc/interp_setup.cc +++ b/src/emc/rs274ngc/interp_setup.cc @@ -180,7 +180,6 @@ setup::setup() : feed_hold(0), loggingLevel(0), debugmask(0), - log_file{}, program_prefix{}, subroutines{}, use_lazy_close(0), From 91dca07a20af7705581e9f83540752ef540751cc Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 02:25:37 +0100 Subject: [PATCH 06/19] interp: keep program_prefix and wizard_root in std::string Both were PATH_MAX char arrays filled by realpath() into the buffer. They are now std::string, filled through a small real_path() helper that lets realpath() allocate the result. When realpath() fails the name is now empty. Before, glibc could leave the part of the path it had resolved in the buffer, and the subroutine search then looked in a directory that does not exist. findFile() only reads its directory and target, so they are const now. --- src/emc/rs274ngc/interp_internal.hh | 4 ++-- src/emc/rs274ngc/interp_o_word.cc | 4 ++-- src/emc/rs274ngc/rs274ngc_interp.hh | 4 ++-- src/emc/rs274ngc/rs274ngc_pre.cc | 30 ++++++++++++++++++++--------- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/emc/rs274ngc/interp_internal.hh b/src/emc/rs274ngc/interp_internal.hh index 1ee2e326729..3a6cfb9f318 100644 --- a/src/emc/rs274ngc/interp_internal.hh +++ b/src/emc/rs274ngc/interp_internal.hh @@ -863,12 +863,12 @@ struct setup bool feed_hold; // feed hold is enabled int loggingLevel; // 0 means logging is off int debugmask; // from INI EMC/DEBUG - char program_prefix[PATH_MAX]; // program directory + std::string program_prefix; // program directory const char *subroutines[MAX_SUB_DIRS]; // subroutines directories int use_lazy_close; // wait until next open before closing // the input file int lazy_closing; // close has been called - char wizard_root[PATH_MAX]; + std::string wizard_root; int tool_change_at_g30; int tool_change_quill_up; int tool_change_with_spindle_on; diff --git a/src/emc/rs274ngc/interp_o_word.cc b/src/emc/rs274ngc/interp_o_word.cc index 828119dc99d..5e87de326e7 100644 --- a/src/emc/rs274ngc/interp_o_word.cc +++ b/src/emc/rs274ngc/interp_o_word.cc @@ -47,8 +47,8 @@ namespace bp = boost::python; */ int Interp::findFile( // ARGUMENTS - char *direct, // the directory to start looking in - char *target, // the name of the file to find + const char *direct, // the directory to start looking in + const char *target, // the name of the file to find char *foundFileDirect) // where to store the result { FILE *file; diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index 5e4dce0d250..328a24d2a96 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -566,8 +566,8 @@ int read_dollar(char *line, int *counter, block_pointer block, // O_word stuff int findFile( // ARGUMENTS - char *direct, // the directory to start looking in - char *target, // the name of the file to find + const char *direct, // the directory to start looking in + const char *target, // the name of the file to find char *foundFileDirect); // where to store the result int control_save_offset( /* ARGUMENTS */ diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 99efe2bd24d..2ec985a6ed1 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -808,6 +808,18 @@ void Interp::set_loglevel(int level) { _setup.loggingLevel = level; } /***********************************************************************/ +// realpath() into a std::string. On failure `out` is left empty. +static bool real_path(const std::string &path, std::string &out) +{ + out.clear(); + char *resolved = realpath(path.c_str(), nullptr); + if (!resolved) + return false; + out = resolved; + free(resolved); + return true; +} + /*! rs274_ngc_init Returned Value: int @@ -967,18 +979,18 @@ int Interp::init() _setup.use_lazy_close = 1; - _setup.wizard_root[0] = 0; + _setup.wizard_root.clear(); if(auto inistring = inifile.findString("WIZARD_ROOT", "WIZARD")) { logDebug("[WIZARD]WIZARD_ROOT:%s", inistring->c_str()); - if (realpath(inistring->c_str(), _setup.wizard_root) == NULL) { + if (!real_path(*inistring, _setup.wizard_root)) { //realpath didn't find the file logDebug("realpath failed to find wizard_root:%s:", inistring->c_str()); } } - logDebug("_setup.wizard_root:%s:", _setup.wizard_root); + logDebug("_setup.wizard_root:%s:", _setup.wizard_root.c_str()); - _setup.program_prefix[0] = 0; + _setup.program_prefix.clear(); if(auto inistring = inifile.findString("PROGRAM_PREFIX", "DISPLAY")) { // found it @@ -986,18 +998,18 @@ int Interp::init() if (inifile.TildeExpansion(*inistring, expandinistring)) { logDebug("TildeExpansion failed for: %s",inistring->c_str()); } - if (realpath(expandinistring.c_str(), _setup.program_prefix) == NULL){ + if (!real_path(expandinistring, _setup.program_prefix)) { //realpath didn't find the file logDebug("realpath failed to find program_prefix:%s:", inistring->c_str()); } logDebug("program prefix:%s: prefix:%s:", - inistring->c_str(), _setup.program_prefix); + inistring->c_str(), _setup.program_prefix.c_str()); } else { logDebug("PROGRAM_PREFIX not found"); } - logDebug("_setup.program_prefix:%s:", _setup.program_prefix); + logDebug("_setup.program_prefix:%s:", _setup.program_prefix.c_str()); if(auto inistring = inifile.findString("SUBROUTINE_PATH", "RS274NGC")) { @@ -2810,7 +2822,7 @@ FILE *Interp::find_ngc_file(setup_pointer settings,const char *basename, char *f if (!newFP) { // expand '~' into user path - wordexp(settings->program_prefix, &exp_result, 0); + wordexp(settings->program_prefix.c_str(), &exp_result, 0); chk = snprintf(newFileName, sizeof(newFileName), "%s/%s", exp_result.we_wordv[0], tmpFileName); // found a file we can open? @@ -2846,7 +2858,7 @@ FILE *Interp::find_ngc_file(setup_pointer settings,const char *basename, char *f int ret; // walks the directory hierarchy ? - ret = findFile(settings->wizard_root, tmpFileName, foundPlace); + ret = findFile(settings->wizard_root.c_str(), tmpFileName, foundPlace); if (INTERP_OK == ret) { // create the long name From 0a6d48b40336e465b4778c2261086a9bddf66d2c Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 02:33:14 +0100 Subject: [PATCH 07/19] interp: keep the error text in the Interp, as a std::string The text of the last error lived in a file-scope static char buffer, so every Interp in a process shared it. It is now the member saved_error_, which also drops the LINELEN limit on its length. getSavedError() returns a const std::string&, and setSavedError() takes a std::string_view. The two callers that copied the text into a fixed buffer before CHKS overwrote it now copy it into a std::string; one of them used an unbounded strcpy. --- src/emc/rs274ngc/interp_convert.cc | 12 ++++++------ src/emc/rs274ngc/interpmodule.cc | 5 ++++- src/emc/rs274ngc/rs274ngc_interp.hh | 6 ++++-- src/emc/rs274ngc/rs274ngc_pre.cc | 25 +++++++++++-------------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/emc/rs274ngc/interp_convert.cc b/src/emc/rs274ngc/interp_convert.cc index 0e11093b768..7a7be0d8ce1 100644 --- a/src/emc/rs274ngc/interp_convert.cc +++ b/src/emc/rs274ngc/interp_convert.cc @@ -3901,9 +3901,9 @@ int Interp::restore_settings(setup_pointer settings, while (s != NULL) { int status = execute(s); if (status != INTERP_OK) { - char currentError[LINELEN+1]; - rs274ngc_strxcpy(currentError,getSavedError()); - CHKS(status, _("M7x: restore_settings failed executing: '%s': %s"), s, currentError); + // a copy: CHKS replaces the saved error while formatting + const std::string currentError = getSavedError(); + CHKS(status, _("M7x: restore_settings failed executing: '%s': %s"), s, currentError.c_str()); } s = strtok_r(NULL, "\n", &stateptr); } @@ -3978,10 +3978,10 @@ int Interp::restore_from_tag(StateTag const &tag) while (s != NULL) { int status = execute(s); if (status != INTERP_OK) { - char currentError[LINELEN+1]; - strcpy(currentError,getSavedError()); + // a copy: CHKS replaces the saved error while formatting + const std::string currentError = getSavedError(); CHKS(status, _("Failed to restore interp state on abort " - "'%s': %s"), s, currentError); + "'%s': %s"), s, currentError.c_str()); } s = strtok_r(NULL, "\n", &stateptr); } diff --git a/src/emc/rs274ngc/interpmodule.cc b/src/emc/rs274ngc/interpmodule.cc index 1597ec1be34..942df978195 100644 --- a/src/emc/rs274ngc/interpmodule.cc +++ b/src/emc/rs274ngc/interpmodule.cc @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include "boost_pyenum_macros.hh" @@ -911,7 +913,8 @@ BOOST_PYTHON_MODULE(interpreter) { .def("set_errormsg", &setErrorMsg) - .def("get_errormsg", &Interp::getSavedError) + .def("get_errormsg", &Interp::getSavedError, + bp::return_value_policy()) .def("stack", &errorStack) .def("synch", &Interp::synch) diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index 328a24d2a96..e57e688fc55 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -681,9 +681,9 @@ int read_inputs(setup_pointer settings); int py_reload(); FILE *find_ngc_file(setup_pointer settings,const char *basename, char *foundhere = NULL); - const char *getSavedError(); + const std::string &getSavedError() const { return saved_error_; } // set error message text without going through printf format interpretation - int setSavedError(const char *msg); + int setSavedError(std::string_view msg); int unwind_call(int status, const char *file, int line, const char *function); @@ -719,6 +719,8 @@ int read_inputs(setup_pointer settings); InterpReturn check_g74_g84_spindle(GCodes motion, CANON_DIRECTION dir); private: + std::string saved_error_; // text of the last error, set by setError + [[nodiscard]] static bool is_parameter_readonly(int index); [[nodiscard]] static bool is_any_m_code_remapped(block_pointer block, setup_pointer settings); diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 2ec985a6ed1..c0ee1f47a4f 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -123,7 +123,6 @@ const char *Interp::interp_status(int status) { extern struct _inittab builtin_modules[]; int trace; -static char savedError[LINELEN+1]; Interp::Interp() : log_file(stderr), @@ -796,7 +795,7 @@ int Interp::exit() ERM("pycall(%s):\n%s", INIT_FUNC, python_plugin->last_exception().c_str()); // this likely wont make it to the UI's any more so bark on stderr - fprintf(stderr, "%s\n",savedError); + fprintf(stderr, "%s\n", saved_error_.c_str()); } } @@ -2361,25 +2360,23 @@ void Interp::print_state_tag(StateTag const &tag) void Interp::setError(const char *fmt, ...) { - va_list ap; + va_list ap, ap2; va_start(ap, fmt); - - vsnprintf(savedError, LINELEN, fmt, ap); - + va_copy(ap2, ap); + int n = vsnprintf(nullptr, 0, fmt, ap); va_end(ap); -} -const char *Interp::getSavedError() -{ - return savedError; + saved_error_.assign(n > 0 ? n : 0, '\0'); + if (n > 0) + vsnprintf(saved_error_.data(), n + 1, fmt, ap2); + va_end(ap2); } // set error message text without going through printf format interpretation -int Interp::setSavedError(const char *msg) +int Interp::setSavedError(std::string_view msg) { - savedError[0] = '\0'; - strncpy(savedError, msg, LINELEN); + saved_error_ = msg; return INTERP_OK; } @@ -2408,7 +2405,7 @@ char * Interp::error_text(int error_code, //!< code number of error { if(error_code == INTERP_ERROR) { - strncpy(error_text, savedError, max_size); + strncpy(error_text, saved_error_.c_str(), max_size); error_text[max_size-1] = 0; return error_text; From 749c04e28f32fb376f040efb347deda1352dcaa9 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 03:12:12 +0100 Subject: [PATCH 08/19] interp: return std::string from the InterpBase text getters error_text, line_text, file_name, stack_name, command and file each filled a caller's buffer and returned it. They now return a std::string. The buffers that existed only for these calls are gone: two globals in emctask.cc, the static savedError in gcodemodule.cc and the ones in sai. canterp keeps its file name in a std::string. file_name() returned an empty name when the name did not fit the buffer. It now returns the whole name, so for a name longer than the NML status field emctask now reports it truncated instead of empty. This changes the InterpBase interface, so an interpreter loaded with [TASK]INTERPRETER has to be rebuilt against the new header. rs274 -g prints the same output as before for all 651 .ngc files under tests/ and nc_files/, four of which end in an interpreter error. --- src/emc/canterp/canterp.cc | 49 ++++++------ src/emc/rs274ngc/gcodemodule.cc | 4 +- src/emc/rs274ngc/interp_base.hh | 14 ++-- src/emc/rs274ngc/rs274ngc_interp.hh | 28 +++---- src/emc/rs274ngc/rs274ngc_pre.cc | 113 ++++++---------------------- src/emc/sai/driver.cc | 23 ++---- src/emc/sai/saicanon.cc | 23 +++--- src/emc/task/emctask.cc | 28 +++---- 8 files changed, 94 insertions(+), 188 deletions(-) diff --git a/src/emc/canterp/canterp.cc b/src/emc/canterp/canterp.cc index cb95320cf91..d54f7afb2ea 100644 --- a/src/emc/canterp/canterp.cc +++ b/src/emc/canterp/canterp.cc @@ -66,11 +66,11 @@ static char the_command_args[LINELEN] = { 0 }; // just the args part class Canterp : public InterpBase { public: - Canterp () : f(NULL), filename{} {} - char *error_text(int errcode, char *buf, size_t buflen) override; - char *stack_name(int index, char *buf, size_t buflen) override; - char *line_text(char *buf, size_t buflen) override; - char *file_name(char *buf, size_t buflen) override; + Canterp () : f(NULL) {} + std::string error_text(int errcode) override; + std::string stack_name(int index) override; + std::string line_text() override; + std::string file_name() override; size_t line_length() override; int sequence_number() override; int ini_load(const char *inifile) override; @@ -87,8 +87,8 @@ class Canterp : public InterpBase { int reset() override; int line() override; int call_level() override; - char *command(char *buf, size_t buflen) override; - char *file(char *buf, size_t buflen) override; + std::string command() override; + std::string file() override; int on_abort(int reason, const char *message) override; void active_g_codes(int active_gcodes[ACTIVE_G_CODES]) override; void active_m_codes(int active_mcodes[ACTIVE_M_CODES]) override; @@ -103,18 +103,15 @@ class Canterp : public InterpBase { void set_loop_on_main_m99(bool state) override; FILE* get_stdout() override { return NULL; }; FILE *f; - char filename[PATH_MAX]; + std::string filename; }; -char *Canterp::error_text(int errcode, char *buf, size_t buflen) { - if(errcode < INTERP_MIN_ERROR) snprintf(buf, buflen, "OK %d", errcode); - else snprintf(buf, buflen, "ERROR %d", errcode); - return buf; +std::string Canterp::error_text(int errcode) { + return (errcode < INTERP_MIN_ERROR ? "OK " : "ERROR ") + std::to_string(errcode); } -char *Canterp::stack_name(int index, char *buf, size_t buflen) { - snprintf(buf, buflen, "", index); - return buf; +std::string Canterp::stack_name(int index) { + return ""; } int Canterp::ini_load(const char * /*inifile*/) { @@ -697,7 +694,7 @@ int Canterp::execute() { int Canterp::open(const char *newfilename) { if(f) fclose(f); f = fopen(newfilename, "r"); - if(f) snprintf(filename, sizeof(filename), "%s", newfilename); + if(f) filename = newfilename; return f ? INTERP_OK : INTERP_ERROR; } @@ -715,17 +712,14 @@ int Canterp::reset() { return 0; } int Canterp::line() { return 0; } int Canterp::call_level() { return 0; } -char *Canterp::line_text(char *buf, size_t bufsize) { - snprintf(buf, bufsize, ""); - return buf; +std::string Canterp::line_text() { + return ""; } -char *Canterp::file_name(char *buf, size_t bufsize) { - snprintf(buf, bufsize, "%s", filename); - return buf; +std::string Canterp::file_name() { + return filename; } -char *Canterp::file(char *buf, size_t bufsize) { - snprintf(buf, bufsize, "%s", filename); - return buf; +std::string Canterp::file() { + return filename; } int Canterp::on_abort(int reason, const char *message) { @@ -733,9 +727,8 @@ int Canterp::on_abort(int reason, const char *message) reset(); return INTERP_OK; } -char *Canterp::command(char *buf, size_t bufsize) { - snprintf(buf, bufsize, ""); - return buf; +std::string Canterp::command() { + return ""; } size_t Canterp::line_length() { return 0; diff --git a/src/emc/rs274ngc/gcodemodule.cc b/src/emc/rs274ngc/gcodemodule.cc index 1f91631ef1f..bf571937b5a 100644 --- a/src/emc/rs274ngc/gcodemodule.cc +++ b/src/emc/rs274ngc/gcodemodule.cc @@ -1079,15 +1079,13 @@ static py::object parse_file(const char *f, py::handle canon, static int maxerror = -1; -static char savedError[LINELEN+1]; static py::str rs274_strerror(int err) { // The text belongs to the interpreter the last parse built; without one // there is nothing to ask, and dereferencing it would end the process. if(!parse_state.pinterp) throw std::runtime_error("gcode.strerror: no interpreter yet - " "call gcode.parse first"); - parse_state.pinterp->error_text(err, savedError, LINELEN); - return py::str(savedError); + return py::str(parse_state.pinterp->error_text(err)); } // One (min, max) box over every point of every move handed in, plus the box diff --git a/src/emc/rs274ngc/interp_base.hh b/src/emc/rs274ngc/interp_base.hh index 03d302a09c1..f85759c2ce7 100644 --- a/src/emc/rs274ngc/interp_base.hh +++ b/src/emc/rs274ngc/interp_base.hh @@ -21,6 +21,7 @@ #include #include +#include #include #include #include "../nml_intf/modal_state.hh" @@ -29,10 +30,11 @@ class InterpBase : boost::noncopyable { public: virtual ~InterpBase(); - virtual char *error_text(int errcode, char *buf, size_t buflen) = 0; - virtual char *line_text(char *buf, size_t buflen) = 0; - virtual char *file_name(char *buf, size_t buflen) = 0; - virtual char *stack_name(int index, char *buf, size_t buflen) = 0; + virtual std::string error_text(int errcode) = 0; + virtual std::string line_text() = 0; + virtual std::string file_name() = 0; + // empty past the end of the call stack + virtual std::string stack_name(int index) = 0; virtual size_t line_length() = 0; virtual int sequence_number() = 0; virtual int ini_load(const char *inifile) = 0; @@ -63,8 +65,8 @@ public: const char ** /*filename*/, const char ** /*subname*/, int * /*line*/) { return -1; } - virtual char *command(char *buf, size_t buflen) = 0; - virtual char *file(char *buf, size_t buflen) = 0; + virtual std::string command() = 0; + virtual std::string file() = 0; virtual int on_abort(int reason, const char *message) = 0; virtual void active_g_codes(int active_gcodes[ACTIVE_G_CODES]) = 0; virtual void active_m_codes(int active_mcodes[ACTIVE_M_CODES]) = 0; diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index e57e688fc55..d84b1abb682 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -95,32 +95,26 @@ public: // Print contents of state tag for debugging void print_state_tag(StateTag const &tag) override; -// copy the text of the error message whose number is error_code into the -// error_text array, but stop at max_size if the text is longer. - char *error_text(int error_code, char *error_text, - size_t max_size) override; +// the text of the error message whose number is error_code + std::string error_text(int error_code) override; void setError(const char *fmt, ...) __attribute__((format(printf,2,3))); -// copy the name of the currently open file into the file_name array, -// but stop at max_size if the name is longer - char *file_name(char *file_name, size_t max_size) override; +// the name of the currently open file + std::string file_name() override; // return the length of the most recently read line size_t line_length() override; -// copy the text of the most recently read line into the line_text array, -// but stop at max_size if the text is longer - char *line_text(char *line_text, size_t max_size) override; +// the text of the most recently read line + std::string line_text() override; // return the current sequence number (how many lines read) int sequence_number() override; -// copy the function name from the stack_index'th position of the -// function call stack at the time of the most recent error into -// the function name string, but stop at max_size if the name is longer - char *stack_name(int stack_index, char *function_name, - size_t max_size) override; +// the function name at the stack_index'th position of the function +// call stack at the time of the most recent error, empty past the end + std::string stack_name(int stack_index) override; // Get the parameter file name from the INI file. int ini_load(const char *filename) override; @@ -131,9 +125,9 @@ public: int resolve_call_stack_frame(int node_id, int level, const char **filename, const char **subname, int *line) override; - char *command(char *buf, size_t len) override { line_text(buf, len); return buf; } + std::string command() override { return line_text(); } - char *file(char *buf, size_t len) override { file_name(buf, len); return buf; } + std::string file() override { return file_name(); } int init_tool_parameters(); int default_tool_parameters(); diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index c0ee1f47a4f..99b247b7d6d 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -2384,69 +2384,39 @@ int Interp::setSavedError(std::string_view msg) /*! Interp::error_text -Returned Value: none +Returned Value: the text of the error error_code: the saved message for +INTERP_ERROR, a fixed text for INTERP_FILE_NOT_OPEN, empty otherwise. -Side Effects: see below +Side Effects: none Called By: external programs -This copies the error string whose index in the _rs274ngc_errors array -is error_code into the error_text array -- unless the error_code is -an out-of-bounds index or the length of the error string is not less -than max_size, in which case an empty string is put into the -error_text. The length of the error_text array should be at least -max_size. - */ -char * Interp::error_text(int error_code, //!< code number of error - char *error_text, //!< char array to copy error text into - size_t max_size) //!< maximum number of characters to copy +std::string Interp::error_text(int error_code) //!< code number of error { - if(error_code == INTERP_ERROR) - { - strncpy(error_text, saved_error_.c_str(), max_size); - error_text[max_size-1] = 0; - - return error_text; - } - else if (error_code == INTERP_FILE_NOT_OPEN) - { - strncpy(error_text, "File not open", max_size); - error_text[max_size-1] = 0; - - return error_text; - } - - error_text[0] = 0; - return error_text; + if (error_code == INTERP_ERROR) + return saved_error_; + if (error_code == INTERP_FILE_NOT_OPEN) + return "File not open"; + return {}; } /***********************************************************************/ /*! Interp::file_name -Returned Value: none +Returned Value: the name of the currently open file -Side Effects: see below +Side Effects: none Called By: external programs -This copies the _setup.filename (the name of the currently open -file) into the file_name array -- unless the name is not shorter than -max_size, in which case a null string is put in the file_name array. - - */ -char *Interp::file_name(char *file_name, //!< string: to copy file name into - size_t max_size) //!< maximum number of characters to copy +std::string Interp::file_name() { - if (strlen(_setup.filename) < ((size_t) max_size)) - strcpy(file_name, _setup.filename); - else - file_name[0] = 0; - return file_name; + return _setup.filename; } /***********************************************************************/ @@ -2470,33 +2440,17 @@ size_t Interp::line_length() /*! Interp::line_text -Returned Value: none +Returned Value: the text of the most recently read line -Side Effects: See below +Side Effects: none Called By: external programs -This copies at most (max_size - 1) non-null characters of the most -recently read line into the line_text string and puts a NULL after the -last non-null character. - */ -char *Interp::line_text(char *line_text, //!< string: to copy line into - size_t max_size) //!< maximum number of characters to copy +std::string Interp::line_text() { - size_t n; - char *the_text; - - the_text = _setup.linetext; - for (n = 0; n < (max_size - 1); n++) { - if (the_text[n] != 0) - line_text[n] = the_text[n]; - else - break; - } - line_text[n] = 0; - return line_text; + return _setup.linetext; } /***********************************************************************/ @@ -2522,18 +2476,13 @@ int Interp::sequence_number() /*! Interp::stack_name -Returned Value: none +Returned Value: the name at position stack_index of the _setup.stack +array, or an empty string if stack_index is out of bounds. -Side Effects: see below +Side Effects: none Called By: external programs -This copies at most (max_size - 1) non-null characters from the -string whose index in the _setup.stack array is stack_index into the -function_name string and puts a NULL after the last non-null character -- -unless the stack_index is an out-of-bounds index, in which case an -empty string is put into the function_name. - This function is intended to be used several times in a row to get the stack of function calls that existed when the most recent error occurred. It should be called first with stack_index equal to 0, @@ -2542,25 +2491,11 @@ empty string is returned for the name. */ -char *Interp::stack_name(int stack_index, //!< index into stack of function names - char *function_name, //!< string: to copy function name into - size_t max_size) //!< maximum number of characters to copy +std::string Interp::stack_name(int stack_index) //!< index into stack of function names { - size_t n; - char *the_name; - - if ((stack_index > -1) && (stack_index < STACK_LEN)) { - the_name = _setup.stack[stack_index]; - for (n = 0; n < (max_size - 1); n++) { - if (the_name[n] != 0) - function_name[n] = the_name[n]; - else - break; - } - function_name[n] = 0; - } else - function_name[0] = 0; - return function_name; + if ((stack_index > -1) && (stack_index < STACK_LEN)) + return _setup.stack[stack_index]; + return {}; } /***********************************************************************/ diff --git a/src/emc/sai/driver.cc b/src/emc/sai/driver.cc index e0b9661f43e..0f401ba0276 100644 --- a/src/emc/sai/driver.cc +++ b/src/emc/sai/driver.cc @@ -109,24 +109,18 @@ void report_error( /* ARGUMENTS */ int error_code, /* the code number of the error message */ int print_stack) /* print stack if ON, otherwise not */ { - char interp_error_text_buf[LINELEN]; - int k; - - error_text(error_code, interp_error_text_buf, 5); /* for coverage of code */ - error_text(error_code, interp_error_text_buf, LINELEN); + const std::string text = error_text(error_code); fprintf(stderr, "%s\n", - ((interp_error_text_buf[0] == 0) ? "Unknown error, bad error code" : interp_error_text_buf)); - line_text(interp_error_text_buf, LINELEN); - fprintf(stderr, "%s\n", interp_error_text_buf); + text.empty() ? "Unknown error, bad error code" : text.c_str()); + fprintf(stderr, "%s\n", line_text().c_str()); if (print_stack == ON) { - for (k = 0; ; k++) + for (int k = 0; ; k++) { - stack_name(k, interp_error_text_buf, LINELEN); - if (interp_error_text_buf[0] != 0) - fprintf(stderr, "%s\n", interp_error_text_buf); - else + const std::string name = stack_name(k); + if (name.empty()) break; + fprintf(stderr, "%s\n", name.c_str()); } } } @@ -741,8 +735,7 @@ int main (int argc, char ** argv) exit(1); } status = interpret_from_file(do_next, block_delete, print_stack); - file_name(buffer, 5); /* called to exercise the function */ - file_name(buffer, 79); /* called to exercise the function */ + file_name(); /* called to exercise the function */ interp_close(); } line_length(); /* called to exercise the function */ diff --git a/src/emc/sai/saicanon.cc b/src/emc/sai/saicanon.cc index b71da2657c8..befb9da5287 100644 --- a/src/emc/sai/saicanon.cc +++ b/src/emc/sai/saicanon.cc @@ -75,26 +75,21 @@ extern InterpBase *pinterp; void print_nc_line_number() { - char text[256]; - int k; - int m; - - pinterp->line_text(text, 256); - for (k = 0; - ((k < 256) && - ((text[k] == '\t') || (text[k] == ' ') || (text[k] == '/'))); - k++); - if ((k < 256) && ((text[k] == 'n') || (text[k] == 'N'))) + // the first 255 characters, as the 256-byte buffer this read into held + const std::string text = pinterp->line_text().substr(0, 255); + size_t k = text.find_first_not_of("\t /"); + if (k == std::string::npos) + k = text.size(); + if (k < text.size() && (text[k] == 'n' || text[k] == 'N')) { fputc('N', _outfile); - for (k++, m = 0; - ((k < 256) && (text[k] >= '0') && (text[k] <= '9')); - k++, m++) + int m = 0; + for (k++; k < text.size() && text[k] >= '0' && text[k] <= '9'; k++, m++) fputc(text[k], _outfile); for (; m < 6; m++) fputc(' ', _outfile); } - else if (k < 256) + else fprintf(_outfile, "N..... "); } diff --git a/src/emc/task/emctask.cc b/src/emc/task/emctask.cc index c9772a266db..84b5241a28f 100644 --- a/src/emc/task/emctask.cc +++ b/src/emc/task/emctask.cc @@ -61,8 +61,6 @@ InterpBase *pinterp=NULL; setup_pointer _is = NULL; // helper for gdb hardware watchpoints FIXME // Print error messages thrown by interpreter -static char interp_error_text_buf[LINELEN]; -static char interp_stack_buf[LINELEN]; static void print_interp_error(int retval) { @@ -75,22 +73,20 @@ static void print_interp_error(int retval) emcStatus->task.interpreter_errcode = retval; } - interp_error_text_buf[0] = 0; - interp.error_text(retval, interp_error_text_buf, LINELEN); - if (0 != interp_error_text_buf[0]) { - log_error("interp_error: {}\n", interp_error_text_buf); + const std::string error_text = interp.error_text(retval); + if (!error_text.empty()) { + log_error("interp_error: {}\n", error_text); } - emcOperatorError("%s", interp_error_text_buf); + emcOperatorError("%s", error_text.c_str()); index = 0; if (emc_debug & EMC_DEBUG_INTERP) { log_info("Interpreter stack: \t"); while (index < 5) { - interp_stack_buf[0] = 0; - interp.stack_name(index, interp_stack_buf, LINELEN); - if (0 == interp_stack_buf[0]) { + const std::string name = interp.stack_name(index); + if (name.empty()) { break; } - log_info(" - {} ", interp_stack_buf); + log_info(" - {} ", name); index++; } log_info("\n"); @@ -664,9 +660,10 @@ int emcTaskPlanLevel() int emcTaskPlanCommand(char *cmd) { - char buf[LINELEN]; - - strcpy(cmd, interp.command(buf, LINELEN)); + // cmd is the LINELEN array emcStatus->task.command + const std::string command = interp.command(); + strncpy(cmd, command.c_str(), LINELEN - 1); + cmd[LINELEN - 1] = 0; log_debug(EMC_DEBUG_INTERP, "emcTaskPlanCommand({}) called. (line_number={})\n", cmd, emcStatus->task.readLine); @@ -695,8 +692,7 @@ int emcTaskUpdate(EMC_TASK_STAT * stat) // currentLine set in main // readLine set in main - char buf[LINELEN]; - rtapi_strxcpy(stat->file, interp.file(buf, LINELEN)); + rtapi_strxcpy(stat->file, interp.file().c_str()); // Report the subroutine call stack of the move motion is executing, not the // one the interpreter has read ahead to -- those are decoupled by up to From a0cee6bbcf02740bc06e5074ef0b4f137ae796e0 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 14:32:52 +0100 Subject: [PATCH 09/19] interp: build file search paths in std::string instead of PATH_MAX buffers find_ngc_file() and findFile() now build their candidate paths with std::string and hand the result back as a std::string, so there is no length limit and no bare strcpy into the caller's buffer. The repeated wordexp() calls go through a small expand_path() helper that frees each result; before, only the last one was freed. A wordexp() failure now falls back to the unexpanded path instead of reading an uninitialised result, and an empty PROGRAM_PREFIX is skipped rather than searched as "(null)/name.ngc". SUBROUTINE_PATH is split with string_view and resolved with real_path(), so a long value is no longer silently truncated. Empty entries are still skipped, and the "too many entries" message now only fires when there really is an extra entry. control_back_to() printed an uninitialised buffer as the file name when a subroutine file was not found; it now prints the .ngc it looked for. save_parameters() reads the old .var file with std::getline and writes lines with fprintf, instead of going through a PATH_MAX-sized line buffer. A missing file still reads as empty, without the /dev/null fallback. interp_from_shlib() builds the plugin path as a std::string. --- src/emc/rs274ngc/interp_base.cc | 13 +- src/emc/rs274ngc/interp_o_word.cc | 44 ++++--- src/emc/rs274ngc/rs274ngc_interp.hh | 8 +- src/emc/rs274ngc/rs274ngc_pre.cc | 177 ++++++++++------------------ 4 files changed, 93 insertions(+), 149 deletions(-) diff --git a/src/emc/rs274ngc/interp_base.cc b/src/emc/rs274ngc/interp_base.cc index 3df5478412f..d8e083dd65c 100644 --- a/src/emc/rs274ngc/interp_base.cc +++ b/src/emc/rs274ngc/interp_base.cc @@ -17,7 +17,6 @@ */ #include "interp_base.hh" #include -#include #include #include @@ -25,8 +24,7 @@ InterpBase::~InterpBase() {} InterpBase *interp_from_shlib(const char *shlib) { void * interp_lib; - char relative_interp[PATH_MAX]; - char const * interp_path; + std::string interp_path; dlopen(NULL, RTLD_GLOBAL); @@ -36,16 +34,15 @@ InterpBase *interp_from_shlib(const char *shlib) { } else { // The passed-in .so name is a relative path or just a bare // filename, look for it in `${EMC2_HOME}/lib/linuxcnc`. - snprintf(relative_interp, sizeof(relative_interp), "%s/%s", EMC2_HOME "/lib/linuxcnc", shlib); - interp_path = relative_interp; + interp_path = std::string(EMC2_HOME "/lib/linuxcnc/") + shlib; } - interp_lib = dlopen(interp_path, RTLD_NOW); + interp_lib = dlopen(interp_path.c_str(), RTLD_NOW); if(!interp_lib) { - fprintf(stderr, "emcTaskInit: could not open interpreter '%s': %s\n", interp_path, dlerror()); + fprintf(stderr, "emcTaskInit: could not open interpreter '%s': %s\n", interp_path.c_str(), dlerror()); return NULL; } - fprintf(stderr, "emcTaskInit: using custom interpreter '%s'\n", interp_path); + fprintf(stderr, "emcTaskInit: using custom interpreter '%s'\n", interp_path.c_str()); typedef InterpBase* (*Constructor)(); Constructor constructor = (Constructor)dlsym(interp_lib, "makeInterp"); diff --git a/src/emc/rs274ngc/interp_o_word.cc b/src/emc/rs274ngc/interp_o_word.cc index 5e87de326e7..8969fa36c1c 100644 --- a/src/emc/rs274ngc/interp_o_word.cc +++ b/src/emc/rs274ngc/interp_o_word.cc @@ -47,35 +47,33 @@ namespace bp = boost::python; */ int Interp::findFile( // ARGUMENTS - const char *direct, // the directory to start looking in - const char *target, // the name of the file to find - char *foundFileDirect) // where to store the result + const std::string &direct, // the directory to start looking in + std::string_view target, // the name of the file to find + std::string &foundFileDirect) // the directory it was found in { FILE *file; DIR *aDir; struct dirent *aFile; - char targetPath[PATH_MAX+1]; - snprintf(targetPath, PATH_MAX, "%s/%s", direct, target); - file = fopen(targetPath, "r"); + std::string targetPath = direct + "/"; + targetPath += target; + file = fopen(targetPath.c_str(), "r"); if (file) { - rs274ngc_strlcpy(foundFileDirect, direct, PATH_MAX); + foundFileDirect = direct; fclose(file); return INTERP_OK; } - aDir = opendir(direct); + aDir = opendir(direct.c_str()); if (!aDir) { ERS(NCE_FILE_NOT_OPEN); } while ((aFile = readdir(aDir))) { if (aFile->d_type == DT_DIR && - (0 != strncmp(aFile->d_name, "..", 3)) && - (0 != strncmp(aFile->d_name, ".", 2))) { + (0 != strcmp(aFile->d_name, "..")) && + (0 != strcmp(aFile->d_name, "."))) { - char path[PATH_MAX+1]; - snprintf(path, PATH_MAX, "%s/%s", direct, aFile->d_name); - if (INTERP_OK == findFile(path, target, foundFileDirect)) { + if (INTERP_OK == findFile(direct + "/" + aFile->d_name, target, foundFileDirect)) { closedir(aDir); return INTERP_OK; } @@ -542,7 +540,7 @@ int Interp::control_back_to( block_pointer block, // pointer to block setup_pointer settings) // pointer to machine settings { static char name[] = "control_back_to"; - char newFileName[PATH_MAX]; + std::string newFileName; FILE *newFP; offset_map_iterator it; offset_pointer op; @@ -588,21 +586,21 @@ int Interp::control_back_to( block_pointer block, // pointer to block } // #2 open the File - newFP = find_ngc_file(settings, block->o_name, newFileName); + newFP = find_ngc_file(settings, block->o_name, &newFileName); if (newFP) { - logOword("fopen: |%s| OK", newFileName); + logOword("fopen: |%s| OK", newFileName.c_str()); settings->sequence_number = 0; // close the old file... if (settings->file_pointer) fclose(settings->file_pointer); settings->file_pointer = newFP; - if (strlen(newFileName) >= sizeof(settings->filename)) { - logOword("new filename '%s' is too long (max len %zu)\n", newFileName, sizeof(settings->filename)-1); - ERS(NCE_UNABLE_TO_OPEN_FILE, newFileName); + if (newFileName.size() >= sizeof(settings->filename)) { + logOword("new filename '%s' is too long (max len %zu)\n", newFileName.c_str(), sizeof(settings->filename)-1); + ERS(NCE_UNABLE_TO_OPEN_FILE, newFileName.c_str()); } - strncpy(settings->filename, newFileName, sizeof(settings->filename)); + rs274ngc_strxcpy(settings->filename, newFileName.c_str()); } else { // No file found for this sub name. // Block forward-seek if the current file is not the main program. @@ -614,12 +612,12 @@ int Interp::control_back_to( block_pointer block, // pointer to block if (main_frame->filename == NULL || strcmp(settings->filename, main_frame->filename) != 0) { ERS(_("Subroutine 'O%s' not found -- " - "not in offset table and no file '%s' found"), - block->o_name, newFileName); + "not in offset table and no file '%s.ngc' found"), + block->o_name, block->o_name); } } char *dirname = getcwd(NULL, 0); - logOword("fopen: |%s| failed CWD:|%s|", newFileName, + logOword("fopen: |%s.ngc| failed CWD:|%s|", block->o_name, dirname); free(dirname); } diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index d84b1abb682..dc91f401caa 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -560,9 +560,9 @@ int read_dollar(char *line, int *counter, block_pointer block, // O_word stuff int findFile( // ARGUMENTS - const char *direct, // the directory to start looking in - const char *target, // the name of the file to find - char *foundFileDirect); // where to store the result + const std::string &direct, // the directory to start looking in + std::string_view target, // the name of the file to find + std::string &foundFileDirect); // the directory it was found in int control_save_offset( /* ARGUMENTS */ // int line, /* (o-word) line number */ @@ -673,7 +673,7 @@ int read_inputs(setup_pointer settings); int calltype); int py_execute(const char *cmd, bool as_file = false); // for (py, ....) comments int py_reload(); - FILE *find_ngc_file(setup_pointer settings,const char *basename, char *foundhere = NULL); + FILE *find_ngc_file(setup_pointer settings, std::string_view basename, std::string *foundhere = nullptr); const std::string &getSavedError() const { return saved_error_; } // set error message text without going through printf format interpretation diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 99b247b7d6d..eccc6813863 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -86,7 +86,9 @@ include an option for suppressing superfluous commands. #include #include #include +#include #include +#include #include #include @@ -1013,38 +1015,34 @@ int Interp::init() if(auto inistring = inifile.findString("SUBROUTINE_PATH", "RS274NGC")) { // found it - int dct; - char* nextdir; - char tmpdirs[PATH_MAX+1]; - - for (dct=0; dct < MAX_SUB_DIRS; dct++) { + for (int dct=0; dct < MAX_SUB_DIRS; dct++) { _setup.subroutines[dct] = NULL; } - rs274ngc_strxcpy(tmpdirs,inistring->c_str()); - char *saveptr; - nextdir = strtok_r(tmpdirs,":",&saveptr); // first token - dct = 0; - while (1) { - char tmp_path[PATH_MAX]; + std::string_view dirs = *inistring; + int dct = 0; + while (!dirs.empty()) { + size_t colon = dirs.find(':'); + std::string nextdir(dirs.substr(0, colon)); + dirs.remove_prefix(colon == dirs.npos ? dirs.size() : colon + 1); + if (nextdir.empty()) + continue; // skip empty entries, as strtok did + if (dct >= MAX_SUB_DIRS) { + logDebug("too many entries in SUBROUTINE_PATH, max=%d", MAX_SUB_DIRS); + break; + } std::string expandnextdir; if (inifile.TildeExpansion(nextdir, expandnextdir)) { - logDebug("TildeExpansion failed for: %s",nextdir); + logDebug("TildeExpansion failed for: %s",nextdir.c_str()); } - if (realpath(expandnextdir.c_str(), tmp_path) == NULL){ + std::string resolved; + if (!real_path(expandnextdir, resolved)) { //realpath didn't find the directory - logDebug("realpath failed to find subroutines[%d]:%s:",dct,nextdir); - _setup.subroutines[dct] = NULL; + logDebug("realpath failed to find subroutines[%d]:%s:",dct,nextdir.c_str()); } else { - _setup.subroutines[dct] = strstore(tmp_path); + _setup.subroutines[dct] = strstore(resolved.c_str()); logDebug("program prefix[%d]:%s",dct,_setup.subroutines[dct]); dct++; } - if (dct >= MAX_SUB_DIRS) { - logDebug("too many entries in SUBROUTINE_PATH, max=%d", MAX_SUB_DIRS); - break; - } - nextdir = strtok_r(NULL,":",&saveptr); - if (nextdir == NULL) break; // no more tokens } } else @@ -2016,9 +2014,8 @@ int Interp::save_parameters(const char *filename, //!< name of file to writ const double parameters[]) //!< parameters to save { FORCE_LC_NUMERIC_C; - FILE *infile; FILE *outfile; - char line[PATH_MAX]; + std::string line; int variable; double value; int required; // number of next required parameter @@ -2029,55 +2026,40 @@ int Interp::save_parameters(const char *filename, //!< name of file to writ outfile = fopen(tempfile.c_str(), "w"); CHKS((outfile == NULL), NCE_CANNOT_OPEN_VARIABLE_FILE); - infile = fopen(filename, "r"); - if(!infile) - infile = fopen("/dev/null", "r"); - if(!infile) { - // If we can't open the 'filename', we may not be able to open "/dev/null" - // either. If so, print an error, close 'outfile' and continue. - perror("Interp::save_parameters(): fopen(/dev/null)"); - fclose(outfile); - return INTERP_OK; - } + // a missing file reads as empty, so only the required parameters are written + std::ifstream infile(filename); k = 0; index = 0; required = required_parameters[index++]; - while (feof(infile) == 0) { - if (fgets(line, sizeof(line), infile) == NULL) { - break; - } + while (std::getline(infile, line)) { // try for a variable-value match - if (sscanf(line, "%d %lf", &variable, &value) == 2) { + if (sscanf(line.c_str(), "%d %lf", &variable, &value) == 2) { CHKS(((variable <= 0) || (variable >= RS274NGC_MAX_PARAMETERS)), NCE_PARAMETER_NUMBER_OUT_OF_RANGE); for (; k < RS274NGC_MAX_PARAMETERS; k++) { if (k > variable) { - fclose(infile); fclose(outfile); ERS(NCE_PARAMETER_FILE_OUT_OF_ORDER); } else if (k == variable) { - snprintf(line, sizeof(line), "%d\t%f\n", k, parameters[k]); - fputs(line, outfile); + fprintf(outfile, "%d\t%f\n", k, parameters[k]); if (k == required) required = required_parameters[index++]; k++; break; } else if (k == required) // know (k < variable) { - snprintf(line, sizeof(line), "%d\t%f\n", k, parameters[k]); - fputs(line, outfile); + fprintf(outfile, "%d\t%f\n", k, parameters[k]); required = required_parameters[index++]; } } } } - fclose(infile); + infile.close(); for (; k < RS274NGC_MAX_PARAMETERS; k++) { if (k == required) { - snprintf(line, sizeof(line), "%d\t%f\n", k, parameters[k]); - fputs(line, outfile); + fprintf(outfile, "%d\t%f\n", k, parameters[k]); required = required_parameters[index++]; } } @@ -2718,6 +2700,19 @@ int Interp::on_abort(int reason, const char *message) return status; } +// expand '~' and variables the way the shell would, keeping the first word +static std::string expand_path(const std::string &path) +{ + wordexp_t exp_result; + std::string out = path; + if (wordexp(path.c_str(), &exp_result, 0) == 0) { + if (exp_result.we_wordc > 0) + out = exp_result.we_wordv[0]; + wordfree(&exp_result); + } + return out; +} + // spun out from interp_o_word so we can use it to test ngc file accessibility during // config file parsing (REMAP... ngc=) // Will expand ~ to user's home path @@ -2726,90 +2721,44 @@ int Interp::on_abort(int reason, const char *message) // 2) tries adding the INI defined program prefix to path // 3) tries adding the INI defined subroutine prefix to path // 4) tries adding the INI defined whizard prefix to path -FILE *Interp::find_ngc_file(setup_pointer settings,const char *basename, char *foundhere ) +FILE *Interp::find_ngc_file(setup_pointer settings, std::string_view basename, std::string *foundhere) { - FILE *newFP = NULL; - char tmpFileName[PATH_MAX+1]; - char newFileName[PATH_MAX+1]; - char foundPlace[PATH_MAX+1]; - int dct; - wordexp_t exp_result; - - // #1 check if this is the full path already - - // expand user path - wordexp(basename, &exp_result, 0); - // add .ngc to expanded path - snprintf(tmpFileName, sizeof(tmpFileName), "%s.ngc", exp_result.we_wordv[0]); - - // copy to newFileName - in case this is the one... - size_t chk = snprintf(newFileName, sizeof(newFileName), "%s", tmpFileName); - - // found a file we can open? - if (chk < sizeof(newFileName)){ - newFP = fopen(newFileName, "r"); - } + // #1 check if this is the full path already: expand the user path, add .ngc + const std::string tmpFileName = expand_path(std::string(basename)) + ".ngc"; + std::string newFileName = tmpFileName; + FILE *newFP = fopen(newFileName.c_str(), "r"); // #2 then look in the program_prefix place - if (!newFP) { - - // expand '~' into user path - wordexp(settings->program_prefix.c_str(), &exp_result, 0); - chk = snprintf(newFileName, sizeof(newFileName), "%s/%s", exp_result.we_wordv[0], tmpFileName); - - // found a file we can open? - if (chk < sizeof(newFileName)){ - newFP = fopen(newFileName, "r"); - } + if (!newFP && !settings->program_prefix.empty()) { + newFileName = expand_path(settings->program_prefix) + "/" + tmpFileName; + newFP = fopen(newFileName.c_str(), "r"); } - + // #3 then look in the list of subroutines prefixes if (!newFP) { - for (dct = 0; dct < MAX_SUB_DIRS; dct++) { + for (int dct = 0; dct < MAX_SUB_DIRS; dct++) { if (!settings->subroutines[dct]) - continue; - - // expand '~' into user path - wordexp(settings->subroutines[dct], &exp_result, 0); - chk = snprintf(newFileName, sizeof(newFileName), "%s/%s", exp_result.we_wordv[0], tmpFileName); - - // found a file we can open? - if (chk < sizeof(newFileName)){ - newFP = fopen(newFileName, "r"); - if (newFP) { - // logOword("fopen: |%s|", newFileName); + continue; + newFileName = expand_path(settings->subroutines[dct]) + "/" + tmpFileName; + newFP = fopen(newFileName.c_str(), "r"); + if (newFP) break; // use first occurrence in dir search - } - } - } + } } // #4 if still not found, search the wizard tree // Wiz directory already expands the '~' to user path if (!newFP) { - int ret; - - // walks the directory hierarchy ? - ret = findFile(settings->wizard_root.c_str(), tmpFileName, foundPlace); - - if (INTERP_OK == ret) { - // create the long name - chk = snprintf(newFileName, sizeof(newFileName), "%s/%s", - foundPlace, tmpFileName); - - // found a file we can open? - if (chk < sizeof(newFileName)){ - newFP = fopen(newFileName, "r"); - } + std::string foundPlace; + if (findFile(settings->wizard_root, tmpFileName, foundPlace) == INTERP_OK) { + newFileName = foundPlace + "/" + tmpFileName; + newFP = fopen(newFileName.c_str(), "r"); } } // pass what we found - if (foundhere && (newFP != NULL)) - strcpy(foundhere, newFileName); - - // Not sure this is needed but the internet told me - wordfree(&exp_result); + if (foundhere && newFP) + *foundhere = newFileName; return newFP; } From 10fb59dd59dca899eff49ade6ea2dbebf01a49c9 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 18:37:27 +0100 Subject: [PATCH 10/19] interp: drop fixed buffers from the error stack, REMAP parsing and comment expansion The error call stack (_setup.stack) only ever holds __PRETTY_FUNCTION__ or "Python", which are string literals, so it now keeps the pointers instead of copying each name into a 256-byte row. The ERM/ERS/ERN macros share an ERROR_STACK_START() helper. stack_name() and the Python error stack stop at the nullptr entry, and the Python side checks the array bound, since stack_index is writable from Python. parse_remap() splits the REMAP line into std::strings instead of copying it into a LINELEN buffer and running strtok_r on it, so a long line is no longer cut short. The limit of 19 options is gone; extra options used to be dropped silently. An empty REMAP line now reports an error instead of passing NULL to strstore(), which threw. convert_param_comment() appends to a std::string. It used to write into a 513-byte buffer without checking the length, so a (DEBUG,...) comment with many parameters could overflow the stack; 120 copies of #1 expand to about 1600 characters. Values longer than VAL_LEN still print as ######. write_state_tag() copies the file name into the fixed-size state_tag_t field with string_view::copy, as that struct is part of a motion message. --- src/emc/rs274ngc/interp_convert.cc | 59 +++++++++++------------------ src/emc/rs274ngc/interp_internal.hh | 41 +++++++++----------- src/emc/rs274ngc/interp_remap.cc | 47 ++++++++++++----------- src/emc/rs274ngc/interp_write.cc | 5 ++- src/emc/rs274ngc/interpmodule.cc | 14 +++---- src/emc/rs274ngc/rs274ngc_interp.hh | 2 +- src/emc/rs274ngc/rs274ngc_pre.cc | 2 +- 7 files changed, 77 insertions(+), 93 deletions(-) diff --git a/src/emc/rs274ngc/interp_convert.cc b/src/emc/rs274ngc/interp_convert.cc index 7a7be0d8ce1..5a05a2c90ec 100644 --- a/src/emc/rs274ngc/interp_convert.cc +++ b/src/emc/rs274ngc/interp_convert.cc @@ -1867,7 +1867,7 @@ int Interp::convert_axis_offsets(int g_code, //!< g_code being executed (mus #define VAL_LEN 30 -int Interp::convert_param_comment(char *comment, char *expanded, int /*len*/) +int Interp::convert_param_comment(const char *comment, std::string &expanded) { FORCE_LC_NUMERIC_C; int i; @@ -1876,10 +1876,10 @@ int Interp::convert_param_comment(char *comment, char *expanded, int /*len*/) int paramNumber; int stat; double value; - char valbuf[VAL_LEN]; // max double length + room - char *v; int found; + expanded.clear(); + while(*comment) { @@ -1925,19 +1925,19 @@ int Interp::convert_param_comment(char *comment, char *expanded, int /*len*/) // not a format string so, // back up to the digit to continue processing comment--; - *expanded++ = '.'; + expanded += '.'; } } else { - *expanded++ = '.'; + expanded += '.'; } } else { - *expanded++ = '%'; + expanded += '%'; } } else if(*comment == '#') @@ -2020,7 +2020,7 @@ int Interp::convert_param_comment(char *comment, char *expanded, int /*len*/) // neither numeric or name logDebug("neither numeric nor name"); // just store the '#' - *expanded++ = '#'; + expanded += '#'; CHKS((*comment == 0), NCE_NAMED_PARAMETER_NOT_TERMINATED); continue; @@ -2028,34 +2028,25 @@ int Interp::convert_param_comment(char *comment, char *expanded, int /*len*/) // we have a parameter -- now insert it // we have the value + // values that print longer than VAL_LEN show as ###### + std::string valbuf = "######"; if(found) { // avoid -0.0/0.0 issues double pvalue = equal(value, 0.0) ? 0.0 : value; - int n = snprintf(valbuf, VAL_LEN, format, pvalue); - bool fail = (n >= VAL_LEN || n < 0); - if(fail) - rs274ngc_strxcpy(valbuf, "######"); - - } - else - { - rs274ngc_strxcpy(valbuf, "######"); - } - logDebug("found:%d value:|%s|", found, valbuf); - - v = valbuf; - while(*v) - { - *expanded++ = *v++; + char buf[VAL_LEN]; + int n = snprintf(buf, VAL_LEN, format, pvalue); + if (n >= 0 && n < VAL_LEN) + valbuf = buf; } + logDebug("found:%d value:|%s|", found, valbuf.c_str()); + expanded += valbuf; } else // not a '#' { - *expanded++ = *comment++; + expanded += *comment++; } } - *expanded = 0; // the final nul return INTERP_OK; } @@ -2094,9 +2085,9 @@ static int startswith(char *haystack, char *needle) { int Interp::convert_comment(char *comment, bool enqueue) //!< string with comment { enum - { LC_SIZE = 256, EX_SIZE = 2*LC_SIZE}; // 256 from comment[256] in rs274ngc.hh + { LC_SIZE = 256 }; // 256 from comment[256] in rs274ngc.hh char lc[LC_SIZE+1]; - char expanded[EX_SIZE+1]; + std::string expanded; char MSG_STR[] = "msg,"; //!!!KL add two -- debug => same as msg @@ -2131,8 +2122,7 @@ int Interp::convert_comment(char *comment, bool enqueue) //!< string with } else if (startswith(lc, DEBUG_STR)) { - convert_param_comment(comment+start+strlen(DEBUG_STR), expanded, - EX_SIZE); + convert_param_comment(comment+start+strlen(DEBUG_STR), expanded); if (_setup.parameters[5599] > 0.0) MESSAGE(expanded); return INTERP_OK; @@ -2141,17 +2131,15 @@ int Interp::convert_comment(char *comment, bool enqueue) //!< string with { FILE *fd = get_stdout(); if (fd) { - convert_param_comment(comment+start+strlen(PRINT_STR), expanded, - EX_SIZE); - fprintf(fd, "%s\n", expanded); + convert_param_comment(comment+start+strlen(PRINT_STR), expanded); + fprintf(fd, "%s\n", expanded.c_str()); fflush(fd); } return INTERP_OK; } else if (startswith(lc, LOG_STR)) { - convert_param_comment(comment+start+strlen(LOG_STR), expanded, - EX_SIZE); + convert_param_comment(comment+start+strlen(LOG_STR), expanded); LOG(expanded); return INTERP_OK; } @@ -2179,8 +2167,7 @@ int Interp::convert_comment(char *comment, bool enqueue) //!< string with } else if (startswith(lc, ABORT_STR)) { - convert_param_comment(comment+start+strlen(ABORT_STR), expanded, - EX_SIZE); + convert_param_comment(comment+start+strlen(ABORT_STR), expanded); setSavedError(expanded); // avoid printf interpretation return INTERP_ERROR; } diff --git a/src/emc/rs274ngc/interp_internal.hh b/src/emc/rs274ngc/interp_internal.hh index 3a6cfb9f318..a33cde2cef3 100644 --- a/src/emc/rs274ngc/interp_internal.hh +++ b/src/emc/rs274ngc/interp_internal.hh @@ -611,7 +611,6 @@ typedef parameter_map::iterator parameter_map_iterator; // flag initialization of r/o parameter #define OVERRIDE_READONLY 1 -#define MAX_REMAPOPTS 20 // current implementation limits - legal modal groups // for M- and G-codes #define M_MODE_OK(m) ((m > 3) && (m < 11)) @@ -697,7 +696,6 @@ and is not represented here */ #define STACK_LEN 50 -#define STACK_ENTRY_LEN 256 #define MAX_SUB_DIRS 10 struct setup @@ -831,7 +829,10 @@ struct setup CANON_SPEED_FEED_MODE speed_feed_mode; // independent or synched bool speed_override[EMCMOT_MAX_SPINDLES]; // whether speed override is enabled CANON_DIRECTION spindle_turning[EMCMOT_MAX_SPINDLES]; // direction spindle is turning - char stack[STACK_LEN][STACK_ENTRY_LEN]; // stack of calls for error reporting + // stack of calls for error reporting: function names, which are string + // literals (__PRETTY_FUNCTION__), so only the pointers are kept. The + // entry after the last one is nullptr. + const char *stack[STACK_LEN]; int stack_index; // index into the stack EmcPose tool_offset; // tool length offset CANON_TOOL_TABLE tool_table[CANON_POCKETS_MAX]; // index is pocket number @@ -965,37 +966,33 @@ static inline void rs274ngc_strlcpy(char *dst, const char *src, size_t dstsize) dst[sizeof(dst)-1] = 0; \ } while(0) +// Start the error call stack with the current function +#define ERROR_STACK_START() \ + do { \ + _setup.stack[0] = __PRETTY_FUNCTION__; \ + _setup.stack[1] = nullptr; \ + _setup.stack_index = 1; \ + } while(0) + // Just set an error string using printf-style formats, do NOT return #define ERM(fmt, ...) \ do { \ setError (fmt, ## __VA_ARGS__); \ - _setup.stack_index = 0; \ - (rs274ngc_strlcpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN)); \ - _setup.stack[_setup.stack_index][STACK_ENTRY_LEN-1] = 0; \ - _setup.stack_index++; \ - _setup.stack[_setup.stack_index][0] = 0; \ + ERROR_STACK_START(); \ } while(0) // Set an error string using printf-style formats and return #define ERS(fmt, ...) \ do { \ setError (fmt, ## __VA_ARGS__); \ - _setup.stack_index = 0; \ - (rs274ngc_strlcpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN)); \ - _setup.stack[_setup.stack_index][STACK_ENTRY_LEN-1] = 0; \ - _setup.stack_index++; \ - _setup.stack[_setup.stack_index][0] = 0; \ + ERROR_STACK_START(); \ return INTERP_ERROR; \ } while(0) // Return one of the very few numeric errors #define ERN(error_code) \ do { \ - _setup.stack_index = 0; \ - (rs274ngc_strlcpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN)); \ - _setup.stack[_setup.stack_index][STACK_ENTRY_LEN-1] = 0; \ - _setup.stack_index++; \ - _setup.stack[_setup.stack_index][0] = 0; \ + ERROR_STACK_START(); \ return error_code; \ } while(0) @@ -1003,11 +1000,9 @@ static inline void rs274ngc_strlcpy(char *dst, const char *src, size_t dstsize) // Propagate an error up the stack #define ERP(error_code) \ do { \ - if (_setup.stack_index < STACK_LEN - 1) { \ - (rs274ngc_strlcpy(_setup.stack[_setup.stack_index], __PRETTY_FUNCTION__, STACK_ENTRY_LEN)); \ - _setup.stack[_setup.stack_index][STACK_ENTRY_LEN-1] = 0; \ - _setup.stack_index++; \ - _setup.stack[_setup.stack_index][0] = 0; \ + if (_setup.stack_index < STACK_LEN - 1) { \ + _setup.stack[_setup.stack_index++] = __PRETTY_FUNCTION__; \ + _setup.stack[_setup.stack_index] = nullptr; \ } \ return error_code; \ } while(0) diff --git a/src/emc/rs274ngc/interp_remap.cc b/src/emc/rs274ngc/interp_remap.cc index 9f197242fcb..4edfcb26697 100644 --- a/src/emc/rs274ngc/interp_remap.cc +++ b/src/emc/rs274ngc/interp_remap.cc @@ -405,46 +405,47 @@ remap_pointer Interp::remapping(const char *code) int Interp::parse_remap(const char *inistring, int lineno) { - char iniline[LINELEN]; - char *argv[MAX_REMAPOPTS]; - int argc = 0; + std::vector argv; + int argc; const char *code; remap r; bool errored = false; int g1 = 0, g2 = 0; int mcode = -1; int gcode = -1; - char *s; memset((void *)&r, 0, sizeof(remap)); r.modal_group = -1; // mark as unset, required param for m/g r.motion_code = INT_MIN; - rs274ngc_strxcpy(iniline, inistring); - // strip trailing comments - if ((s = strchr(iniline, '#')) != NULL) { - *s = '\0'; - } - char* saveptr; - s = strtok_r((char *) iniline, " \t", &saveptr); - - while( s != NULL && argc < MAX_REMAPOPTS - 1) { - argv[argc++] = s; - s = strtok_r( NULL, " \t", &saveptr); + { + std::string_view line = inistring; + // strip trailing comments + line = line.substr(0, line.find('#')); + // split on blanks and tabs + while (true) { + size_t start = line.find_first_not_of(" \t"); + if (start == line.npos) + break; + line.remove_prefix(start); + size_t end = line.find_first_of(" \t"); + argv.emplace_back(line.substr(0, end)); + line.remove_prefix(end == line.npos ? line.size() : end); + } } - if (argc == MAX_REMAPOPTS) { - Error("parse_remap: too many arguments (max %d)", MAX_REMAPOPTS); + argc = argv.size(); + if (argc == 0) { + Error("parse_remap: no code to remap: %d:REMAP = %s", lineno, inistring); goto fail; } - argv[argc] = NULL; - code = strstore(argv[0]); + code = strstore(argv[0].c_str()); r.name = code; for (int i = 1; i < argc; i++) { int kwlen = 0; - char *kw = argv[i]; - char *arg = strchr(argv[i],'='); + char *kw = argv[i].data(); + char *arg = strchr(kw,'='); if (arg != NULL) { - kwlen = arg - argv[i]; + kwlen = arg - kw; arg++; if (!strlen(arg)) { // 'kw=' Error("option '%s' - zero length value: %d:REMAP = %s", @@ -616,7 +617,7 @@ int Interp::parse_remap(const char *inistring, int lineno) } if (!G_MODE_OK(r.modal_group)) { Error("error: code '%s' : %s modalgroup= given : %d:REMAP = %s", - argv[0], + argv[0].c_str(), r.modal_group == -1 ? "no" : "invalid", lineno, inistring); diff --git a/src/emc/rs274ngc/interp_write.cc b/src/emc/rs274ngc/interp_write.cc index 63dc9b73b0b..8af67ed6930 100644 --- a/src/emc/rs274ngc/interp_write.cc +++ b/src/emc/rs274ngc/interp_write.cc @@ -242,7 +242,10 @@ int Interp::write_state_tag(block_pointer block, bool in_sub = (settings->call_level > 0 && settings->remap_level == 0); bool external_sub = strcmp(settings->filename, settings->sub_context[0].filename); - rs274ngc_strxcpy(state.filename, settings->filename); + // state_tag_t travels in a motion message, so its filename stays fixed-size + size_t len = std::string_view(settings->filename) + .copy(state.filename, sizeof(state.filename) - 1); + state.filename[len] = 0; state.flags[GM_FLAG_IN_REMAP] = in_remap; state.flags[GM_FLAG_IN_SUB] = in_sub; diff --git a/src/emc/rs274ngc/interpmodule.cc b/src/emc/rs274ngc/interpmodule.cc index 942df978195..bebca993750 100644 --- a/src/emc/rs274ngc/interpmodule.cc +++ b/src/emc/rs274ngc/interpmodule.cc @@ -102,12 +102,9 @@ static void setErrorMsg(Interp &interp, const char *s) if ((s == NULL) || (strlen(s) == 0)) s = "###"; interp.setError ("%s", s); - settings->stack_index = 0; - strncpy(settings->stack[settings->stack_index], - "Python", STACK_ENTRY_LEN); - settings->stack[settings->stack_index][STACK_ENTRY_LEN-1] = 0; - settings->stack_index++; - settings->stack[settings->stack_index][0] = 0; + settings->stack[0] = "Python"; + settings->stack[1] = nullptr; + settings->stack_index = 1; } static bp::object errorStack(Interp &interp) @@ -115,8 +112,9 @@ static bp::object errorStack(Interp &interp) setup *settings = &interp._setup; bp::list msgs; - for (int i = 0; i < settings->stack_index; i++) - msgs.append(bp::object( (const char *) settings->stack[i])); + // stack_index is writable from Python, so stay inside the array + for (int i = 0; i < settings->stack_index && i < STACK_LEN && settings->stack[i]; i++) + msgs.append(bp::object(settings->stack[i])); return msgs; } diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index dc91f401caa..859cfcbe272 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -259,7 +259,7 @@ public: char arc_axis2(CANON_PLANE plane); int convert_axis_offsets(int g_code, block_pointer block, setup_pointer settings); - int convert_param_comment(char *comment, char *expanded, int len); + int convert_param_comment(const char *comment, std::string &expanded); int convert_comment(char *comment, bool enqueue = true); int convert_control_mode(int g_code, double tolerance, double naivecam_tolerance, double r_word, bool r_present, /* G64_R_PLANNER */ diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index eccc6813863..3eeac40cee1 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -2475,7 +2475,7 @@ empty string is returned for the name. std::string Interp::stack_name(int stack_index) //!< index into stack of function names { - if ((stack_index > -1) && (stack_index < STACK_LEN)) + if ((stack_index > -1) && (stack_index < STACK_LEN) && _setup.stack[stack_index]) return _setup.stack[stack_index]; return {}; } From 737af9607189fea3914076b60ab5c65d6e1b5d2d Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 29 Oct 2026 23:51:28 +0100 Subject: [PATCH 11/19] interp: build the remap call line in a std::string convert_remapped_code() built "O call [v1][v2]..." in a char cmd[LINELEN], and add_parameters() strcat'ed each positional argument onto it without a length check. A remap with an '@' argspec and large word values, e.g. M400 P[10**300] Q[10**300], overflowed the stack buffer and crashed. The line is now built in a std::string. A line that is too long is rejected by read() with "Command too long" instead. --- src/emc/rs274ngc/interp_remap.cc | 14 +++++++------- src/emc/rs274ngc/rs274ngc_interp.hh | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/emc/rs274ngc/interp_remap.cc b/src/emc/rs274ngc/interp_remap.cc index 4edfcb26697..3a9f1bd299f 100644 --- a/src/emc/rs274ngc/interp_remap.cc +++ b/src/emc/rs274ngc/interp_remap.cc @@ -113,7 +113,7 @@ int Interp::convert_remapped_code(block_pointer /*block*/, int status; block_pointer cblock; bp::list plist; - char cmd[LINELEN]; + std::string cmd; if (number == -1) logRemap("convert_remapped_code '%c'", letter); @@ -156,7 +156,7 @@ int Interp::convert_remapped_code(block_pointer /*block*/, // oword mechanism - so no duplication of handler calling code // is needed. - snprintf(cmd, sizeof(cmd),"O <%s> call ", REMAP_FUNC(remap)); + cmd = std::string("O <") + REMAP_FUNC(remap) + "> call "; // the controlling block holds all dynamic remap information. cblock = &CONTROLLING_BLOCK(*settings); @@ -168,16 +168,16 @@ int Interp::convert_remapped_code(block_pointer /*block*/, // if user specified '@' // named local params are dealt with in execute_call() when // the new call frame is fully established - CHP(add_parameters(settings, cblock, &cmd[strlen(cmd)])); + CHP(add_parameters(settings, cblock, &cmd)); } if ((_setup.debugmask & EMC_DEBUG_REMAP) && (_setup.loggingLevel > 2)) { - logRemap("convert_remapped_code(%s)", cmd); + logRemap("convert_remapped_code(%s)", cmd.c_str()); } // good to go, pass to o-word call handling mechanism - status = read(cmd); + status = read(cmd.c_str()); block_pointer eblock = &EXECUTING_BLOCK(*settings); eblock->call_type = CT_REMAP; CHKS(status != INTERP_OK, @@ -217,7 +217,7 @@ int Interp::convert_remapped_code(block_pointer /*block*/, int Interp::add_parameters(setup_pointer settings, block_pointer cblock, - char *posarglist) + std::string *posarglist) { const char *code; block_pointer block; @@ -281,7 +281,7 @@ int Interp::add_parameters(setup_pointer settings, if (posarglist) { char actual[LINELEN]; snprintf(actual, sizeof(actual), "[%.4lf]", value); - strcat(posarglist, actual); + *posarglist += actual; cblock->param_cnt++; } else { diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index 859cfcbe272..8f524fe21fc 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -635,7 +635,7 @@ int read_inputs(setup_pointer settings); // add named params/param dict if argspec given // present optional words to the subroutine's local variables and Py dict int add_parameters(setup_pointer settings, block_pointer cblock, - char *posarglist); + std::string *posarglist); int init_named_parameters(); int init_python_predef_parameter(const char *name); From b8c9b6de07097074ec028aec1a6c1280ab254170 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:08:37 +0100 Subject: [PATCH 12/19] interp, task: review cleanups - nurbs_math.hh: drop the redundant extern on the prototypes. - restore_parameters() and save_parameters() take the file name as a const std::string &, so the callers no longer need .c_str(). - print_interp_error(): indent the whole function with spaces. --- src/emc/nml_intf/nurbs_math.hh | 50 ++++++++++++++--------------- src/emc/rs274ngc/rs274ngc_interp.hh | 6 ++-- src/emc/rs274ngc/rs274ngc_pre.cc | 27 ++++++++-------- src/emc/task/emctask.cc | 26 +++++++-------- 4 files changed, 54 insertions(+), 55 deletions(-) diff --git a/src/emc/nml_intf/nurbs_math.hh b/src/emc/nml_intf/nurbs_math.hh index 790984c1e4a..659f8566ecc 100644 --- a/src/emc/nml_intf/nurbs_math.hh +++ b/src/emc/nml_intf/nurbs_math.hh @@ -15,66 +15,66 @@ /* Additional functions needed to calculate nurbs G5 points */ -extern std::vector nurbs_G5_knot_vector_creator(unsigned int n, unsigned int k); +std::vector nurbs_G5_knot_vector_creator(unsigned int n, unsigned int k); -extern double nurbs_G5_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector); +double nurbs_G5_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector); -extern double nurbs_G5_Rden(double u, unsigned int k, +double nurbs_G5_Rden(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector); -extern NURBS_PLANE_POINT nurbs_G5_point(double u, unsigned int k, +NURBS_PLANE_POINT nurbs_G5_point(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector); -extern NURBS_PLANE_POINT nurbs_G5_tangent(double u, unsigned int k, +NURBS_PLANE_POINT nurbs_G5_tangent(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector); /* Additional functions needed to calculate nurbs points G_6_2*/ -extern std::vector nurbs_g6_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& nurbs_control_points); +std::vector nurbs_g6_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& nurbs_control_points); std::vector nurbs_interval_span_knot_vector_creator(unsigned int n, unsigned int k, const std::vector& knot_vector_); -extern double nurbs_lderv(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +double nurbs_lderv(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); -extern double nurbs_Sa1_b1_length_(double a1, double b1, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +double nurbs_Sa1_b1_length_(double a1, double b1, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); -extern std::vector nurbs_lenght_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); +std::vector nurbs_lenght_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); -extern double nurbs_lenght_tot(int j, const std::vector& span_knot_vector, const std::vector& lenght_vector); +double nurbs_lenght_tot(int j, const std::vector& span_knot_vector, const std::vector& lenght_vector); -extern double nurbs_lenght_l_u(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector, const std::vector& lenght_vector); +double nurbs_lenght_l_u(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector, const std::vector& lenght_vector); -extern std::vector nurbs_Du_span_knot_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); +std::vector nurbs_Du_span_knot_vector_creator(unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector& span_knot_vector); -extern std::vector nurbs_costant_crator(const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& Du_span_knot_vector); +std::vector nurbs_costant_crator(const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& Du_span_knot_vector); -extern double nurbs_uj_l(double l, const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& nurbs_costant); +double nurbs_uj_l(double l, const std::vector& span_knot_vector, const std::vector& lenght_vector, const std::vector& nurbs_costant); /* Funzioni di supporto all'algoritmo di suddivisione */ -extern std::vector nurbs_G6_new_control_point_nurbs1(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); +std::vector nurbs_G6_new_control_point_nurbs1(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); -extern std::vector nurbs_G6_new_control_point_nurbs2(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); +std::vector nurbs_G6_new_control_point_nurbs2(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_ ); -extern std::vector nurbs_G6_knot_vector_new_creator_sgment(unsigned int k, const std::vector& nurbs_control_points); +std::vector nurbs_G6_knot_vector_new_creator_sgment(unsigned int k, const std::vector& nurbs_control_points); /* ... */ -extern double nurbs_G6_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector_); +double nurbs_G6_Nmix(unsigned int i, unsigned int k, double u, const std::vector& knot_vector_); -extern std::vector< std::vector > nurbs_G6_Nmix_creator(double u, unsigned int k, double n, const std::vector& knot_vector_); +std::vector< std::vector > nurbs_G6_Nmix_creator(double u, unsigned int k, double n, const std::vector& knot_vector_); -extern double nurbs_G6_Rden(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +double nurbs_G6_Rden(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); -extern NURBS_PLANE_POINT nurbs_G6_point(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +NURBS_PLANE_POINT nurbs_G6_point(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); -extern double nurbs_G6_Rdenx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); +double nurbs_G6_Rdenx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); -extern NURBS_PLANE_POINT nurbs_G6_pointx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); +NURBS_PLANE_POINT nurbs_G6_pointx(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_, const std::vector< std::vector >& A6); -extern NURBS_PLANE_POINT nurbs_G6_point_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +NURBS_PLANE_POINT nurbs_G6_point_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); -extern NURBS_PLANE_POINT nurbs_G6_tangent_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); +NURBS_PLANE_POINT nurbs_G6_tangent_x(double u, unsigned int k, const std::vector& nurbs_control_points, const std::vector& knot_vector_); #endif diff --git a/src/emc/rs274ngc/rs274ngc_interp.hh b/src/emc/rs274ngc/rs274ngc_interp.hh index 8f524fe21fc..9c8526cdc3f 100644 --- a/src/emc/rs274ngc/rs274ngc_interp.hh +++ b/src/emc/rs274ngc/rs274ngc_interp.hh @@ -62,11 +62,11 @@ public: int reset() override; // restore interpreter variables from a file - int restore_parameters(const char *filename); + int restore_parameters(const std::string &filename); // save interpreter variables to file - int save_parameters(const char *filename, - const double parameters[]); + int save_parameters(const std::string &filename, + const double parameters[]); // synchronize your internal model with the external world int synch() override; diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 3eeac40cee1..66c3360e772 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -779,7 +779,7 @@ written. Otherwise, the default parameter file name is used. int Interp::exit() { - save_parameters(parameter_file().c_str(), _setup.parameters); + save_parameters(parameter_file(), _setup.parameters); reset(); // interpreter shutdown Python hook @@ -1117,7 +1117,7 @@ int Interp::init() } USE_LENGTH_UNITS(_setup.length_units); - CHP(restore_parameters(parameter_file().c_str())); + CHP(restore_parameters(parameter_file())); pars = _setup.parameters; _setup.origin_index = (int) (pars[5220] + 0.0001); if(_setup.origin_index < 1 || _setup.origin_index > 9) { @@ -1917,7 +1917,7 @@ sets of origin offsets. Any parameter not given a value in the file has its value set to zero. */ -int Interp::restore_parameters(const char *filename) //!< name of parameter file to read +int Interp::restore_parameters(const std::string &filename) //!< name of parameter file to read { FORCE_LC_NUMERIC_C; FILE *infile; @@ -1931,11 +1931,11 @@ int Interp::restore_parameters(const char *filename) //!< name of parameter fi // it's OK if the parameter file doesn't exist yet // it'll be created in due course with some default values - if(access(filename, F_OK) == -1) + if(access(filename.c_str(), F_OK) == -1) return INTERP_OK; // open original for reading - infile = fopen(filename, "r"); - CHKS((infile == NULL), _("Unable to open parameter file: '%s'"), filename); + infile = fopen(filename.c_str(), "r"); + CHKS((infile == NULL), _("Unable to open parameter file: '%s'"), filename.c_str()); pars = _setup.parameters; k = 0; @@ -2010,8 +2010,8 @@ If a required parameter is missing from the input file, this does not complain, but does write it in the output file. */ -int Interp::save_parameters(const char *filename, //!< name of file to write - const double parameters[]) //!< parameters to save +int Interp::save_parameters(const std::string &filename, //!< name of file to write + const double parameters[]) //!< parameters to save { FORCE_LC_NUMERIC_C; FILE *outfile; @@ -2022,7 +2022,7 @@ int Interp::save_parameters(const char *filename, //!< name of file to writ int index; // index into _required_parameters int k; - std::string tempfile = std::string(filename) + ".new"; + std::string tempfile = filename + ".new"; outfile = fopen(tempfile.c_str(), "w"); CHKS((outfile == NULL), NCE_CANNOT_OPEN_VARIABLE_FILE); @@ -2067,12 +2067,11 @@ int Interp::save_parameters(const char *filename, //!< name of file to writ fflush(outfile); fdatasync(fileno(outfile)); fclose(outfile); - std::string bakfile = std::string(filename) - + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX; + std::string bakfile = filename + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX; unlink(bakfile.c_str()); - if(link(filename, bakfile.c_str()) < 0) + if(link(filename.c_str(), bakfile.c_str()) < 0) perror("link (updating variable file)"); - if(rename(tempfile.c_str(), filename) < 0) + if(rename(tempfile.c_str(), filename.c_str()) < 0) perror("rename (updating variable file)"); return INTERP_OK; } @@ -2138,7 +2137,7 @@ int Interp::synch() _setup.spindle_mode[s] = SPINDLE_MODE::CONSTANT_RPM; _setup.css_maximum[s] = 0.0; } - save_parameters(parameter_file().c_str(), _setup.parameters); + save_parameters(parameter_file(), _setup.parameters); load_tool_table(); /* must set _setup.tool_max first */ diff --git a/src/emc/task/emctask.cc b/src/emc/task/emctask.cc index 84b5241a28f..2b4490aca48 100644 --- a/src/emc/task/emctask.cc +++ b/src/emc/task/emctask.cc @@ -66,30 +66,30 @@ static void print_interp_error(int retval) { int index = 0; if (retval == 0) { - return; + return; } if (NULL != emcStatus) { - emcStatus->task.interpreter_errcode = retval; + emcStatus->task.interpreter_errcode = retval; } const std::string error_text = interp.error_text(retval); if (!error_text.empty()) { - log_error("interp_error: {}\n", error_text); + log_error("interp_error: {}\n", error_text); } emcOperatorError("%s", error_text.c_str()); index = 0; if (emc_debug & EMC_DEBUG_INTERP) { - log_info("Interpreter stack: \t"); - while (index < 5) { - const std::string name = interp.stack_name(index); - if (name.empty()) { - break; - } - log_info(" - {} ", name); - index++; - } - log_info("\n"); + log_info("Interpreter stack: \t"); + while (index < 5) { + const std::string name = interp.stack_name(index); + if (name.empty()) { + break; + } + log_info(" - {} ", name); + index++; + } + log_info("\n"); } } From d8fc823b7a960ce46d3acba7b099ae795a78484e Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:09:13 +0100 Subject: [PATCH 13/19] interp: free the wordexp() result after WRDE_NOSPACE POSIX says a WRDE_NOSPACE failure may leave a partial result that still has to be freed. After any other error the result is untouched, so it is zero-initialised and only freed in those two cases. --- src/emc/rs274ngc/rs274ngc_pre.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 66c3360e772..564eac771d5 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -2702,13 +2702,15 @@ int Interp::on_abort(int reason, const char *message) // expand '~' and variables the way the shell would, keeping the first word static std::string expand_path(const std::string &path) { - wordexp_t exp_result; + wordexp_t exp_result{}; std::string out = path; - if (wordexp(path.c_str(), &exp_result, 0) == 0) { - if (exp_result.we_wordc > 0) - out = exp_result.we_wordv[0]; + int rc = wordexp(path.c_str(), &exp_result, 0); + if (rc == 0 && exp_result.we_wordc > 0) + out = exp_result.we_wordv[0]; + // after WRDE_NOSPACE the partial result still has to be freed; + // after any other error it is left untouched + if (rc == 0 || rc == WRDE_NOSPACE) wordfree(&exp_result); - } return out; } From 9af83aed7675185acca6fb8b5318a23db193e085 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:12:09 +0100 Subject: [PATCH 14/19] interp: make stack_index read-only from Python The interpreter.stack_index property had a setter, so a remap could set it to a negative value and the next ERP would write before the start of the error stack. Nothing in the tree writes it, the example configs only read it, so the setter is removed. ERP also checks the lower bound now. --- src/emc/rs274ngc/interp_internal.hh | 3 ++- src/emc/rs274ngc/interpmodule.cc | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/emc/rs274ngc/interp_internal.hh b/src/emc/rs274ngc/interp_internal.hh index a33cde2cef3..ca02fa43494 100644 --- a/src/emc/rs274ngc/interp_internal.hh +++ b/src/emc/rs274ngc/interp_internal.hh @@ -1000,7 +1000,8 @@ static inline void rs274ngc_strlcpy(char *dst, const char *src, size_t dstsize) // Propagate an error up the stack #define ERP(error_code) \ do { \ - if (_setup.stack_index < STACK_LEN - 1) { \ + if (_setup.stack_index >= 0 \ + && _setup.stack_index < STACK_LEN - 1) { \ _setup.stack[_setup.stack_index++] = __PRETTY_FUNCTION__; \ _setup.stack[_setup.stack_index] = nullptr; \ } \ diff --git a/src/emc/rs274ngc/interpmodule.cc b/src/emc/rs274ngc/interpmodule.cc index bebca993750..773a12e7065 100644 --- a/src/emc/rs274ngc/interpmodule.cc +++ b/src/emc/rs274ngc/interpmodule.cc @@ -112,7 +112,6 @@ static bp::object errorStack(Interp &interp) setup *settings = &interp._setup; bp::list msgs; - // stack_index is writable from Python, so stay inside the array for (int i = 0; i < settings->stack_index && i < STACK_LEN && settings->stack[i]; i++) msgs.append(bp::object(settings->stack[i])); return msgs; @@ -787,9 +786,6 @@ static inline void set_spindle_turning(Interp &interp, int spindle, int value) static inline int get_stack_index (Interp &interp) { return interp._setup.stack_index; } -static inline void set_stack_index(Interp &interp, int value) { - interp._setup.stack_index = value; -} static inline int get_value_returned (Interp &interp) { return interp._setup.value_returned; } @@ -1019,7 +1015,7 @@ BOOST_PYTHON_MODULE(interpreter) { .add_property("spindle_mode", &get_spindle_mode, &set_spindle_mode) .add_property("num_spindles", &get_num_spindles, &set_num_spindles) .add_property("spindle_turning", &get_spindle_turning, &set_spindle_turning) - .add_property("stack_index", &get_stack_index, &set_stack_index) + .add_property("stack_index", &get_stack_index) .add_property("value_returned", &get_value_returned, &set_value_returned) .add_property("current_tool", &get_current_tool, &set_current_tool) From 1abfd5460b28a3b903c037c24ec0b7c078622b49 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:12:58 +0100 Subject: [PATCH 15/19] canterp, sai: stop the error stack loop SAI's report_error() prints stack names until it gets an empty one, but Canterp::stack_name() returned "" for every index, so rs274 with canterp and -s never stopped printing. Canterp has no call stack and now returns an empty name, and SAI stops after STACK_LEN entries in any case. --- src/emc/canterp/canterp.cc | 5 +++-- src/emc/sai/driver.cc | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/emc/canterp/canterp.cc b/src/emc/canterp/canterp.cc index d54f7afb2ea..8cf41d17d5f 100644 --- a/src/emc/canterp/canterp.cc +++ b/src/emc/canterp/canterp.cc @@ -110,8 +110,9 @@ std::string Canterp::error_text(int errcode) { return (errcode < INTERP_MIN_ERROR ? "OK " : "ERROR ") + std::to_string(errcode); } -std::string Canterp::stack_name(int index) { - return ""; +// canterp has no call stack; an empty name ends the caller's loop +std::string Canterp::stack_name(int /*index*/) { + return {}; } int Canterp::ini_load(const char * /*inifile*/) { diff --git a/src/emc/sai/driver.cc b/src/emc/sai/driver.cc index 0f401ba0276..39ea1cd4fe7 100644 --- a/src/emc/sai/driver.cc +++ b/src/emc/sai/driver.cc @@ -115,7 +115,8 @@ void report_error( /* ARGUMENTS */ fprintf(stderr, "%s\n", line_text().c_str()); if (print_stack == ON) { - for (int k = 0; ; k++) + // a plugin interpreter may never return an empty name + for (int k = 0; k < STACK_LEN; k++) { const std::string name = stack_name(k); if (name.empty()) From ba6b810628b979fb83801247a6dc89eea27eb0c4 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:14:02 +0100 Subject: [PATCH 16/19] interp: close the parameter files on every error path save_parameters() returned on an out-of-range parameter number without closing the .new file, and left the half-written file behind on both of its error paths. It now holds the file in a unique_ptr and removes the temporary file on an error, so the old .var file stays as it is. It keeps stdio for the fdatasync(). restore_parameters() had the same leak on an out-of-range number. It now reads with std::ifstream, like save_parameters() already does. --- src/emc/rs274ngc/rs274ngc_pre.cc | 50 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 564eac771d5..155ab4d2ce7 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -87,6 +87,7 @@ include an option for suppressing superfluous commands. #include #include #include +#include #include #include #include @@ -1920,8 +1921,7 @@ has its value set to zero. int Interp::restore_parameters(const std::string &filename) //!< name of parameter file to read { FORCE_LC_NUMERIC_C; - FILE *infile; - char line[256]; + std::string line; int variable; double value; int required; // number of next required parameter @@ -1934,25 +1934,21 @@ int Interp::restore_parameters(const std::string &filename) //!< name of paramet if(access(filename.c_str(), F_OK) == -1) return INTERP_OK; // open original for reading - infile = fopen(filename.c_str(), "r"); - CHKS((infile == NULL), _("Unable to open parameter file: '%s'"), filename.c_str()); + std::ifstream infile(filename); + CHKS((!infile), _("Unable to open parameter file: '%s'"), filename.c_str()); pars = _setup.parameters; k = 0; index = 0; required = required_parameters[index++]; - while (feof(infile) == 0) { - if (fgets(line, 256, infile) == NULL) { - break; - } + while (std::getline(infile, line)) { // try for a variable-value match in the file - if (sscanf(line, "%d %lf", &variable, &value) == 2) { + if (sscanf(line.c_str(), "%d %lf", &variable, &value) == 2) { CHKS(((variable <= 0) || (variable >= RS274NGC_MAX_PARAMETERS)), NCE_PARAMETER_NUMBER_OUT_OF_RANGE); for (; k < RS274NGC_MAX_PARAMETERS; k++) { if (k > variable) { - fclose(infile); ERS(NCE_PARAMETER_FILE_OUT_OF_ORDER); } else if (k == variable) { pars[k] = value; @@ -1969,7 +1965,7 @@ int Interp::restore_parameters(const std::string &filename) //!< name of paramet } } } - fclose(infile); + infile.close(); for (; k < RS274NGC_MAX_PARAMETERS; k++) { pars[k] = 0; } @@ -2014,7 +2010,6 @@ int Interp::save_parameters(const std::string &filename, //!< name of file to wr const double parameters[]) //!< parameters to save { FORCE_LC_NUMERIC_C; - FILE *outfile; std::string line; int variable; double value; @@ -2022,9 +2017,15 @@ int Interp::save_parameters(const std::string &filename, //!< name of file to wr int index; // index into _required_parameters int k; + // stdio rather than a stream, for fdatasync() below std::string tempfile = filename + ".new"; - outfile = fopen(tempfile.c_str(), "w"); - CHKS((outfile == NULL), NCE_CANNOT_OPEN_VARIABLE_FILE); + std::unique_ptr outfile(fopen(tempfile.c_str(), "w"), fclose); + CHKS((!outfile), NCE_CANNOT_OPEN_VARIABLE_FILE); + // on an error, drop the half-written file and keep the old one + auto discard = [&]() { + outfile.reset(); + unlink(tempfile.c_str()); + }; // a missing file reads as empty, so only the required parameters are written std::ifstream infile(filename); @@ -2035,22 +2036,23 @@ int Interp::save_parameters(const std::string &filename, //!< name of file to wr while (std::getline(infile, line)) { // try for a variable-value match if (sscanf(line.c_str(), "%d %lf", &variable, &value) == 2) { - CHKS(((variable <= 0) - || (variable >= RS274NGC_MAX_PARAMETERS)), - NCE_PARAMETER_NUMBER_OUT_OF_RANGE); + if ((variable <= 0) || (variable >= RS274NGC_MAX_PARAMETERS)) { + discard(); + ERS(NCE_PARAMETER_NUMBER_OUT_OF_RANGE); + } for (; k < RS274NGC_MAX_PARAMETERS; k++) { if (k > variable) { - fclose(outfile); + discard(); ERS(NCE_PARAMETER_FILE_OUT_OF_ORDER); } else if (k == variable) { - fprintf(outfile, "%d\t%f\n", k, parameters[k]); + fprintf(outfile.get(), "%d\t%f\n", k, parameters[k]); if (k == required) required = required_parameters[index++]; k++; break; } else if (k == required) // know (k < variable) { - fprintf(outfile, "%d\t%f\n", k, parameters[k]); + fprintf(outfile.get(), "%d\t%f\n", k, parameters[k]); required = required_parameters[index++]; } } @@ -2059,14 +2061,14 @@ int Interp::save_parameters(const std::string &filename, //!< name of file to wr infile.close(); for (; k < RS274NGC_MAX_PARAMETERS; k++) { if (k == required) { - fprintf(outfile, "%d\t%f\n", k, parameters[k]); + fprintf(outfile.get(), "%d\t%f\n", k, parameters[k]); required = required_parameters[index++]; } } - fflush(outfile); - fdatasync(fileno(outfile)); - fclose(outfile); + fflush(outfile.get()); + fdatasync(fileno(outfile.get())); + outfile.reset(); std::string bakfile = filename + RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX; unlink(bakfile.c_str()); if(link(filename.c_str(), bakfile.c_str()) < 0) From 3b7b61ebe466314cc1d52fa0e586e06490449cd2 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:15:21 +0100 Subject: [PATCH 17/19] task: print the current orientation for an RPY comment without values When (RPY ...) did not have three numbers, the branch meant to print the current orientation printed the PM_RPY it had just failed to read into, which is uninitialised. It now prints the stored orientation converted to roll, pitch and yaw. --- src/emc/task/emccanon.cc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/emc/task/emccanon.cc b/src/emc/task/emccanon.cc index 37d8a418d39..ae3a97c5aab 100644 --- a/src/emc/task/emccanon.cc +++ b/src/emc/task/emccanon.cc @@ -3163,20 +3163,21 @@ void COMMENT(std::string_view comment) // set RPY orientation for subsequent moves if (comment.starts_with("RPY")) { - PM_RPY rpy; // it's RPY

std::istringstream args{std::string(comment)}; std::string word; - if (!(args >> word >> rpy.r >> rpy.p >> rpy.y)) { - // print current orientation - printf("rpy = %f %f %f, quat = %f %f %f %f\n", - rpy.r, rpy.p, rpy.y, quat.s, quat.x, quat.y, quat.z); - } else { - // set and print orientation + double r, p, y; + PM_RPY rpy; + if (args >> word >> r >> p >> y) { + // set the new orientation + rpy = PM_RPY(r, p, y); quat = rpy; - printf("rpy = %f %f %f, quat = %f %f %f %f\n", - rpy.r, rpy.p, rpy.y, quat.s, quat.x, quat.y, quat.z); + } else { + // only show the current one + rpy = quat; } + printf("rpy = %f %f %f, quat = %f %f %f %f\n", + rpy.r, rpy.p, rpy.y, quat.s, quat.x, quat.y, quat.z); return; } // open probe output file From 2bb44bc9307cfeb1c5796b7d2d6306650d6fc2c0 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Sun, 1 Nov 2026 19:24:38 +0100 Subject: [PATCH 18/19] interp: reject REMAP lines that would have no effect parse_remap() accepted a number of REMAP lines that could never work: - a code outside the remappable range, like M1000 or G1, was stored but never looked up - M06 was stored under that name, but the built-in M6 looks for "M6" when it checks for recursion, so the remap called itself again - trailing characters after the number (M6x, G88.12) were ignored - an unknown option only printed a message All of these now fail at startup with the INI line. M-code remaps are stored as M, so M06 and M6 are the same remap and a second one is reported as a duplicate. A failed REMAP line also sets the error text, so init no longer reports "Unknown error, bad error code". The unrecognized-option message printed the whole rest of the line, because it used a field width instead of a precision. tests/remap/introspect had REMAP=M123, which fell in the user M-code range and never did anything. It is now M423. --- src/emc/rs274ngc/interp_remap.cc | 54 ++++++++++++++++++++++++-------- tests/remap/introspect/test.ini | 2 +- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/emc/rs274ngc/interp_remap.cc b/src/emc/rs274ngc/interp_remap.cc index 3a9f1bd299f..d6cb492d449 100644 --- a/src/emc/rs274ngc/interp_remap.cc +++ b/src/emc/rs274ngc/interp_remap.cc @@ -537,8 +537,9 @@ int Interp::parse_remap(const char *inistring, int lineno) r.remap_py = strstore(arg); continue; } - Error("unrecognized option '%*s' in %d:REMAP = %s", + Error("unrecognized option '%.*s' in %d:REMAP = %s", kwlen,kw,lineno,inistring); + errored = true; } if (errored) { goto fail; @@ -575,12 +576,26 @@ int Interp::parse_remap(const char *inistring, int lineno) _setup.remaps[code] = r; break; - case 'm': - if (sscanf(code + 1, "%d", &mcode) != 1) { + case 'm': { + int len = 0; + if (sscanf(code + 1, "%d%n", &mcode, &len) != 1 || code[1 + len] != '\0') { Error("parsing M-code: expecting integer like 'M420', got '%s' : %d:REMAP = %s", code,lineno,inistring); goto fail; } + if (!is_m_code_remappable(mcode)) { + Error("code '%s' can't be remapped : %d:REMAP = %s", + code,lineno,inistring); + goto fail; + } + // the built-in code looks M-code remaps up as "M6", so store M06 as that + code = strstore(("M" + std::to_string(mcode)).c_str()); + r.name = code; + if (_setup.m_remapped[mcode]) { + Error("code '%s' already remapped : %d:REMAP = %s", + code,lineno,inistring); + goto fail; + } if (r.modal_group == -1) { Error("warning: code '%s' : no modalgroup= given, using default group %d : %d:REMAP = %s", code, MCODE_DEFAULT_MODAL_GROUP,lineno,inistring); @@ -594,21 +609,31 @@ int Interp::parse_remap(const char *inistring, int lineno) _setup.remaps[code] = r; _setup.m_remapped[mcode] = &_setup.remaps[code]; break; - case 'g': - + } + case 'g': { + int len = 0; // code may be G88.1 or so - normalize to use 'G881' instead // (multiply by 10, no dots) - if (sscanf(code + 1, "%d.%d", &g1, &g2) == 2) { + if (sscanf(code + 1, "%d.%1d%n", &g1, &g2, &len) == 2) { gcode = g1 * 10 + g2; - } - if ( gcode == -1) { - if (sscanf(code + 1, "%d", &gcode) != 1) { - Error("code '%s' : can\'t parse G-code : %d:REMAP = %s", - code, lineno, inistring); - goto fail; - } + } else if (sscanf(code + 1, "%d%n", &gcode, &len) == 1) { gcode *= 10; } + if (gcode < 0 || code[1 + len] != '\0') { + Error("code '%s' : can\'t parse G-code : %d:REMAP = %s", + code, lineno, inistring); + goto fail; + } + if (!is_g_code_remappable(gcode)) { + Error("code '%s' can't be remapped : %d:REMAP = %s", + code,lineno,inistring); + goto fail; + } + if (_setup.g_remapped[gcode]) { + Error("code '%s' already remapped : %d:REMAP = %s", + code,lineno,inistring); + goto fail; + } r.motion_code = gcode; if (r.modal_group == -1) { Error("warning: code '%s' : no modalgroup= given, using default group %d : %d:REMAP = %s", @@ -626,6 +651,7 @@ int Interp::parse_remap(const char *inistring, int lineno) _setup.remaps[code] = r; _setup.g_remapped[gcode] = &_setup.remaps[code]; break; + } default: // make sure the python plugin is in a usable state if needed @@ -640,5 +666,7 @@ int Interp::parse_remap(const char *inistring, int lineno) return INTERP_OK; fail: + // the details went to the log above; init reports this text + setError("invalid REMAP in INI line %d: %s", lineno, inistring); return INTERP_ERROR; } diff --git a/tests/remap/introspect/test.ini b/tests/remap/introspect/test.ini index 2f621f0f8e5..efb14f93c38 100644 --- a/tests/remap/introspect/test.ini +++ b/tests/remap/introspect/test.ini @@ -5,7 +5,7 @@ DEBUG=0 SUBROUTINE_PATH = . LOG_LEVEL=0 INI_VARS = 1 -REMAP= M123 ngc=foo modalgroup=5 argspec=Pq +REMAP= M423 ngc=foo modalgroup=5 argspec=Pq [PYTHON] PATH_PREPEND=. From e7573e9f7ff42409d62dcc92de683423b7581c61 Mon Sep 17 00:00:00 2001 From: Rene Hopf Date: Thu, 5 Nov 2026 02:19:43 +0100 Subject: [PATCH 19/19] interp: always free the wordexp() result exp_result is zero-initialised, and after an error wordexp() either leaves a partial result (WRDE_NOSPACE) or restores what was passed in, so wordfree() is safe whatever it returned. Calling it unconditionally does not depend on which errors leave something behind. --- src/emc/rs274ngc/rs274ngc_pre.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/emc/rs274ngc/rs274ngc_pre.cc b/src/emc/rs274ngc/rs274ngc_pre.cc index 155ab4d2ce7..6d45e31d559 100644 --- a/src/emc/rs274ngc/rs274ngc_pre.cc +++ b/src/emc/rs274ngc/rs274ngc_pre.cc @@ -2706,13 +2706,10 @@ static std::string expand_path(const std::string &path) { wordexp_t exp_result{}; std::string out = path; - int rc = wordexp(path.c_str(), &exp_result, 0); - if (rc == 0 && exp_result.we_wordc > 0) + if (wordexp(path.c_str(), &exp_result, 0) == 0 && exp_result.we_wordc > 0) out = exp_result.we_wordv[0]; - // after WRDE_NOSPACE the partial result still has to be freed; - // after any other error it is left untouched - if (rc == 0 || rc == WRDE_NOSPACE) - wordfree(&exp_result); + // exp_result starts zeroed, so freeing it is safe whatever wordexp() returned + wordfree(&exp_result); return out; }