diff --git a/check-linuxcnc-browser-app-source-map.sh b/check-linuxcnc-browser-app-source-map.sh index d13a684..7be57d2 100755 --- a/check-linuxcnc-browser-app-source-map.sh +++ b/check-linuxcnc-browser-app-source-map.sh @@ -23,6 +23,12 @@ grep -F 'std::string tempfile = std::string(filename) + ".new";' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'unlink(bakfile.c_str());' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(link(filename, bakfile.c_str()) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null @@ -33,6 +39,8 @@ grep -F '`RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \ docs/linuxcnc-browser-app-source-map.md >/dev/null grep -F '`parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", ...)`' \ docs/linuxcnc-browser-app-source-map.md >/dev/null +grep -F 'orders parameter saves as backup unlink' \ + docs/linuxcnc-browser-app-source-map.md >/dev/null grep -F '' web/index.html >/dev/null grep -F '' web/index.html >/dev/null @@ -63,6 +71,8 @@ grep -F 'app frame did not persist LinuxCNC parameter state into OPFS' \ web/test-browser-wasm-smoke-app-sections.js >/dev/null grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \ web/test-browser-wasm-smoke-app-sections.js >/dev/null +grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \ + web/test-browser-wasm-smoke-app-sections.js >/dev/null grep -F 'OPFS PROGRAM RESTORED' web/test-browser-wasm-smoke-app-sections.js >/dev/null grep -F 'app frame did not reload LinuxCNC parameter state from OPFS after reload' \ web/test-browser-wasm-smoke-app-sections.js >/dev/null diff --git a/check-linuxcnc-opfs-source-map.sh b/check-linuxcnc-opfs-source-map.sh index 46d0ba5..b4098e8 100755 --- a/check-linuxcnc-opfs-source-map.sh +++ b/check-linuxcnc-opfs-source-map.sh @@ -22,6 +22,12 @@ grep -F 'std::string tempfile = std::string(filename) + ".new";' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'unlink(bakfile.c_str());' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(link(filename, bakfile.c_str()) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \ docs/linuxcnc-opfs-source-map.md >/dev/null @@ -31,6 +37,8 @@ grep -F '`Interp::restore_parameters()` for parameter-file reads' \ docs/linuxcnc-opfs-source-map.md >/dev/null grep -F '`Interp::save_parameters()` for `filename + ".new"`' \ docs/linuxcnc-opfs-source-map.md >/dev/null +grep -F 'unlinks `filename + ".bak"`, links the' \ + docs/linuxcnc-opfs-source-map.md >/dev/null grep -F 'must not add independent filesystem behavior or expand the temporary smoke parser' \ docs/linuxcnc-opfs-source-map.md >/dev/null grep -F 'parseFileWithParameterFile()' docs/linuxcnc-opfs-source-map.md >/dev/null @@ -47,6 +55,8 @@ grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`' web/src/wasm-core.js >/dev/nu grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`' web/src/wasm-core.js >/dev/null grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null +grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \ + web/test-browser-wasm-smoke-app-sections.js >/dev/null grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \ web/src/app.js >/dev/null @@ -83,6 +93,7 @@ required_phrases = [ "parameters/rs274ngc.var", "LinuxCNC `read_text()`", "`.new`, `.bak`, failure, and stale-state semantics", + 'unlinks `filename + ".bak"`', "OPFS/WASM mirror persistence", "app-level OPFS program, parameter, backup, and reload behavior", ] diff --git a/check-linuxcnc-wasm-smoke-source-map.sh b/check-linuxcnc-wasm-smoke-source-map.sh index 4fbd002..02d9882 100755 --- a/check-linuxcnc-wasm-smoke-source-map.sh +++ b/check-linuxcnc-wasm-smoke-source-map.sh @@ -19,6 +19,12 @@ grep -F 'int Interp::save_parameters(const char *filename,' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'std::string tempfile = std::string(filename) + ".new";' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'unlink(bakfile.c_str());' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(link(filename, bakfile.c_str()) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null +grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \ + "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'int read_text(const char *command, FILE * inport, char *raw_line,' \ @@ -51,6 +57,7 @@ grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-smoke-so grep -F 'OPFS-only' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null grep -F '`src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage' \ docs/linuxcnc-wasm-smoke-source-map.md >/dev/null +grep -F 'unlinking `filename + ".bak"`' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null grep -F '`src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`' \ docs/linuxcnc-wasm-smoke-source-map.md >/dev/null grep -F 'Generated switchkins config smoke cases must continue to come from' \ @@ -102,6 +109,8 @@ grep -F 'app frame did not persist current program into OPFS' \ web/test-browser-wasm-smoke-app-sections.js >/dev/null grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \ web/test-browser-wasm-smoke-app-sections.js >/dev/null +grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \ + web/test-browser-wasm-smoke-app-sections.js >/dev/null if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2 @@ -145,6 +154,7 @@ required_phrases = [ "Node smoke must leave OPFS disabled", "Generated switchkins config smoke cases", "browser smoke shell guard also pins required section names", + 'unlinking `filename + ".bak"`', "./check-linuxcnc-wasm-smoke-source-map.sh", ] for phrase in required_phrases: diff --git a/docs/linuxcnc-browser-app-source-map.md b/docs/linuxcnc-browser-app-source-map.md index e827488..474b49d 100644 --- a/docs/linuxcnc-browser-app-source-map.md +++ b/docs/linuxcnc-browser-app-source-map.md @@ -15,6 +15,8 @@ must not expand the temporary smoke parser. - `src/emc/rs274ngc/rs274ngc_pre.cc` saves parameter files through `Interp::save_parameters()`, including `filename + ".new"` and `filename + ".bak"` behavior. +- `src/emc/rs274ngc/rs274ngc_pre.cc` orders parameter saves as backup unlink, + backup link, then temporary-file rename. - `src/emc/rs274ngc/rs274ngc_pre.cc` reads program blocks through `read_text()` after the browser bridge mirrors the OPFS program into the WASM filesystem. diff --git a/docs/linuxcnc-opfs-source-map.md b/docs/linuxcnc-opfs-source-map.md index 248285b..5810c81 100644 --- a/docs/linuxcnc-opfs-source-map.md +++ b/docs/linuxcnc-opfs-source-map.md @@ -14,6 +14,9 @@ persistence. It is a policy and bridge map only; it must not add independent fil - `src/emc/rs274ngc/rs274ngc_pre.cc` implements `Interp::save_parameters()` for `filename + ".new"`, backup, and replacement writes. +- `src/emc/rs274ngc/rs274ngc_pre.cc` unlinks `filename + ".bak"`, links the + previous parameter file to that backup path, then renames `filename + ".new"` + over the configured parameter file. ## OPFS Coverage diff --git a/docs/linuxcnc-wasm-smoke-source-map.md b/docs/linuxcnc-wasm-smoke-source-map.md index 2c9d636..f6a15d5 100644 --- a/docs/linuxcnc-wasm-smoke-source-map.md +++ b/docs/linuxcnc-wasm-smoke-source-map.md @@ -11,6 +11,9 @@ OPFS-only is the required boundary. - `src/emc/rs274ngc/rs274ngc_pre.cc` provides `Interp::ini_load()`, `Interp::restore_parameters()`, `Interp::save_parameters()`, and `read_text()` behavior exercised by Node/browser bridge tests. +- `src/emc/rs274ngc/rs274ngc_pre.cc` saves parameters by writing + `filename + ".new"`, unlinking `filename + ".bak"`, linking the previous file + to `filename + ".bak"`, and renaming the temporary file over the main file. - `src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage for M66 waits and M68 analog output side effects used by generated remap cases. - `src/emc/nml_intf/canon.hh` declares the `WAIT()` and diff --git a/web/test-browser-wasm-smoke-app-sections.js b/web/test-browser-wasm-smoke-app-sections.js index 903b5b6..42b9dec 100644 --- a/web/test-browser-wasm-smoke-app-sections.js +++ b/web/test-browser-wasm-smoke-app-sections.js @@ -39,6 +39,13 @@ export async function runBrowserAppOpfsSections(context) { const updatedParameterFile = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var")); expectIncludes(updatedParameterFile, "5161\t2.000000", "app frame did not replace LinuxCNC parameter state after second parse"); + // LinuxCNC source basis: rs274ngc_pre.cc save_parameters() + // renames filename + ".new" over filename after a successful save. + await context.expectOpfsMissing( + appSimulator, + "parameters/rs274ngc.var.new", + "app frame did not clean up LinuxCNC temporary parameter file in OPFS", + ); const savedParameterBackup = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var.bak")); expectIncludes(savedParameterBackup, "5161\t1.000000", "app frame did not persist LinuxCNC parameter backup into OPFS");