fix: input redirect nofollow, history symlink protection, SIGWINCH

ober

d829def572f6d6367d9433774342b64951a56a2c

diff --git a/history.ss b/history.ss
index 1e167eb..f9aafba 100644
--- a/history.ss
+++ b/history.ss
@@ -602,22 +602,29 @@
                      (checked-file (check-untainted-file-path file)))
                  (dynamic-wind
                   (lambda ()
-                    (set! port (open-output-file (check-untainted-file-path checked-file) 'truncate)))
+                    ;; History contains every command typed — paths, hostnames,
+                    ;; the occasional secret pasted in by accident. Open with
+                    ;; O_NOFOLLOW so a symlink planted at $HISTFILE fails with
+                    ;; ELOOP instead of writing through to its target, and set
+                    ;; mode 0600 atomically at creation rather than via a racy
+                    ;; chmod-after-open.
+                    (let ((fd (ffi-open-raw checked-file
+                                            (bitwise-ior O_WRONLY O_CREAT O_TRUNC O_NOFOLLOW)
+                                            #o600)))
+                      (unless (< fd 0)
+                        (set! port (open-fd-output-port fd
+                                                        (buffer-mode block)
+                                                        (native-transcoder))))))
                   (lambda ()
-                   (for-each
-                    (lambda (entry)
-                      (display (history-entry-command entry) port)
-                      (newline port))
-                    entries))
+                    (when port
+                      (for-each
+                       (lambda (entry)
+                         (display (history-entry-command entry) port)
+                         (newline port))
+                       entries)))
                   (lambda ()
                     (when port
-                      (close-output-port port))))
-               ;; History contains every command typed — paths, hostnames,
-               ;; the occasional secret pasted in by accident. Other users
-               ;; on the box have no business reading it. We chmod even
-               ;; though umask is usually 0022, because users routinely
-               ;; unset umask and we don't want to inherit their choice.
-               (ffi-chmod checked-file #o600))))))))))
+                      (close-output-port port)))))))))))))
 
 ;; L-3: bound per-line length. read-line on a multi-GB single-line
 ;; file would attempt to allocate the whole thing. 64KiB is far past
diff --git a/main.ss b/main.ss
index 70dd76d..9569df8 100644
--- a/main.ss
+++ b/main.ss
@@ -627,9 +627,9 @@
          ((string=? sig-name "CHLD")
           (job-update-status!)
           (job-notify!))
-         ;; WINCH: terminal resize — update COLUMNS/LINES
-         ((string=? sig-name "WINCH")
-          (void))  ;; TODO: update terminal dimensions
+          ;; WINCH: terminal resize — refresh cached terminal dimensions
+          ((string=? sig-name "WINCH")
+           (set-terminal-columns! (detect-terminal-columns)))
          (else (void)))
        ;; Check trap table for this signal
        ;; Save and restore $? so trap doesn't affect main flow
diff --git a/redirect.ss b/redirect.ss
index 9ad354d..59fd8b3 100644
--- a/redirect.ss
+++ b/redirect.ss
@@ -229,7 +229,7 @@
       ;; < file — input from file
       ((<)
        (let ((save (save-fd fd)))
-         (redirect-fd-to-file! fd target-str O_RDONLY 0)
+         (redirect-fd-to-file! fd target-str (apply-redir-flags env O_RDONLY) 0)
          ;; Create Gambit port for builtins
          (when (= fd 0)
            (current-input-port (open-input-file (check-untainted-file-path target-str))))
@@ -561,7 +561,7 @@
                                  (error #f ": No such file or directory"))
                                w))))))))
     (case op
-      ((<) (redirect-fd-to-file! fd target-str O_RDONLY 0)
+      ((<) (redirect-fd-to-file! fd target-str (apply-redir-flags env O_RDONLY) 0)
            (when (= fd 0)
              (current-input-port (open-input-file (check-untainted-file-path target-str)))))
       ((>) (let* ((noclobber? (env-option? env "noclobber"))