• fseditor.js: don't dereference line[l+1] after splice in unwrap_line

    From Steven Philley@VERT to GitLab note in main/sbbs on Thu Sep 10 13:34:01 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10298

    ```suggestion:-2+3
    if(line[l+1]==undefined)
    line.push(new Line);
    else {
    ```

    ---
    Synchronet Vertrauen Home of Synchronet [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to GitLab note in main/sbbs on Fri Sep 11 04:43:21 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10307

    Is there a set of steps to reproduce this issue so it can be confirmed as fixed?

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Steven Philley@VERT to GitLab note in main/sbbs on Fri Sep 11 09:43:34 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10312

    Deuce: yes — here's a focused repro for #1183 / this MR.

    **Original crash (production):** full-screen editor (`fseditor.js`) threw `TypeError: line[l + 1] is undefined` in `unwrap_line()` (reported on cvs.synchro.net, Node 12, user Cru Jones). Root cause in the issue: the unkludge path can `splice()` out `line[l+1]` mid-loop, then the "get first word(s) of next line" step still reads `line[l+1].text` with no re-check. Worst case is when that next line was the **last** line.

    **Interactive repro (terminal):**
    1. Enter message editor that uses `exec/fseditor.js` (full-screen editor).
    2. 2. Type enough text that word-wrap creates a **kludged** wrap near the end (a long unbroken token/word that forces a mid-word wrap works well).
    3. 3. Continue until the **final** wrapped fragment is short enough that an unwrap can pull all of it up into the previous line.
    4. 4. Edit so unwrap runs on that kludged line (backspace/delete near the wrap boundary, or any edit that triggers `rewrap`/`unwrap_line`).
    5. 5. Pre-fix: TypeError / node error referencing `unwrap_line` / `line[l + 1] is undefined`.
    6. 6. Post-fix: editor stays up; last line is joined; no TypeError.

    **Why this MR fixes it:** after the unkludge block we restore the loop invariant:
    ```
    if(line[l+1]==undefined)
    break;
    ```
    before the second dereference. The obsolete TODO at the splice is removed.

    Happy to adjust wording or add a tiny jsexec harness case if you want something more automated than the interactive path.

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to GitLab note in main/sbbs on Fri Sep 11 16:31:47 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10337

    So, in an 80-column window, I typed a single "word" of 90 characters.

    I deleted various bits to get it down to 79 chars, but fseditor never threw an error.

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Steven Philley@VERT to GitLab note in main/sbbs on Sat Sep 12 05:10:15 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10344

    @Deuce Thanks for trying that — you're right that the 90-char word → delete-to-79 path does not hit the crash.

    The TypeError only happens when **unkludge** consumes the entire next line and splices it out while that next line is the **last** line, then the loop continues into `line[l+1].text`.

    Minimal state (80-col, usable width 79):

    * `line[0]`: 70 chars, `kludged=true` (mid-word wrap)
    * \- `line[1]`: last line, short no-whitespace suffix that fits in the remaining space (e.g. `XYZTAIL`)

    Without the guard after unkludge: `TypeError: Cannot read properties of undefined (reading 'text')`.

    With the MR guard `if(line[l+1]==undefined) break;`): joins to one line, no throw.

    A long unbroken word that you then shorten to 79 never leaves a kludged line whose unkludge splice removes the final line, so it won't throw — sorry the earlier interactive steps pointed at the wrong path.

    Happy to walk a jsexec/unit harness if that's easier than reproducing inside the full editor.

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Steven Philley@VERT to GitLab note in main/sbbs on Sat Sep 12 05:16:42 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10345

    Follow-up with a stock interactive path that \*does\* hit the crash (char-by-char delete to 79 does not):

    ---
    Synchronet Vertrauen Home of Synchronet [vert/cvs/bbs].synchro.net
  • From Steven Philley@VERT to GitLab note in main/sbbs on Sat Sep 12 08:51:52 2026
    https://gitlab.synchro.net/main/sbbs/-/merge_requests/731#note_10347

    Clarifying after Deuce’s IRC note: you do **not** type a 70-character word to create the kludge.

    Interactive setup:

    1. 80-col window.
    2. 2\. Type one unbroken word of **\~90+** characters so fseditor mid-word wraps → first line \~79 chars (kludged) + remainder on the next line.
    3. 3\. CTRL-W once on that **first** (kludged) line, then any keystroke that triggers rewrap/unwrap.

    The “70-char + short last line” numbers in the harness are the **already-wrapped** in-memory state used to unit-test the crash after a large shrink (e.g. CTRL-W), not the characters you type to get the wrap in the first place. A word ≤79 never mid-word wraps; ≥80 is what creates `kludged`.

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net