Cumulative season's silent-bug hunting (~62 fixes) across the FiveSql2 SQL engine, the Five compiler/runtime, and the hbrdd RDD layer. Saved as a single checkpoint before refactoring the parser to delegate xBase command translation to the preprocessor. Highlights: FiveSql2 engine (_FiveSql2/src/) - prefix-glob index attach -> explicit convention (<table>_pk.ntx, <table>_uq.ntx, <table>.cdx) — fixes silent multi-row INSERT row-drop - DROP/CREATE TABLE FErase chain extended (.cdx, .fsc, .fsv, .dbt, .fpt) - COUNT(DISTINCT col) parsed + aggregated via hSeen hash - UNION column-count mismatch returns SQL_ERR_GRAMMAR (was silent) - DISTINCT + ORDER BY hidden-col leak fixed (trim before DISTINCT) - Derived table FROM (SELECT...) + JOIN right-side derived - Self-FK CASCADE depth 2+ via SqlGetSingleColPK pre-collect - LAG/LEAD default arg uses SqlEvalRowExpr (handles -N const exprs) - DATE literal round-trip validation (Feb 29 non-leap rejected) - CREATE OR REPLACE VIEW; CREATE VIEW errors on already-exists - AlterTable type dispatcher comma-wrapped (1-char type "A" no longer matches CHARACTER) Compiler / runtime - gengo: HB_ -> FV_ prefix on emitted Go function names (Five identity) - gengo split: emit_block.go, emit_stmt.go, folding.go extracted - parser/stmtreg.go nudges - hbrt: debug TUI/CLI restructure (debugcmd, debugkey, termios_*), windows debug stubs collapsed - thread/vm/value/class/pcinterp tightening from panic traces RDD layer (hbrdd/) - dbf: null bitmap support (null.go + null_test.go), mmap split (mmap_posix.go / mmap_windows.go), byte-level numeric parse - ntx/cdx: windows mmap parity - workarea + mem RDD: cross-area state-bleed fixes RTL (hbrtl/) - errorlog rewrite with platform-specific FD (errorlog_fd_unix / errorlog_fd_other) - sqlscan, sqlhelpers, indexrtl, datetime extensions Gates green at checkpoint: - go test ./... : PASS - FiveSql2 SQL:1999 : 43/43 - Harbour compat : 56/56 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
125 lines
3.7 KiB
Go
125 lines
3.7 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
//go:build windows
|
|
|
|
// Windows console equivalent of the Unix termios helpers. Rather than
|
|
// dealing with ReadConsoleInput's VK_* keycodes we turn on
|
|
// ENABLE_VIRTUAL_TERMINAL_INPUT / PROCESSING on modern Windows (10+),
|
|
// which makes the console speak ANSI — same byte stream the Unix path
|
|
// sees. Everything above this file stays platform-neutral.
|
|
|
|
package hbrt
|
|
|
|
import (
|
|
"os"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
enableProcessedInput = 0x0001
|
|
enableLineInput = 0x0002
|
|
enableEchoInput = 0x0004
|
|
enableVirtualTerminalInput = 0x0200
|
|
|
|
enableProcessedOutput = 0x0001
|
|
enableVirtualTerminalProcessing = 0x0004
|
|
)
|
|
|
|
var (
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
|
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
|
)
|
|
|
|
type smallRect struct {
|
|
Left, Top, Right, Bottom int16
|
|
}
|
|
|
|
type consoleScreenBufferInfo struct {
|
|
Size struct{ X, Y int16 }
|
|
CursorPosition struct{ X, Y int16 }
|
|
Attributes uint16
|
|
Window smallRect
|
|
MaximumWindowSize struct{ X, Y int16 }
|
|
}
|
|
|
|
func getConsoleMode(h syscall.Handle) (uint32, bool) {
|
|
var mode uint32
|
|
r, _, _ := procGetConsoleMode.Call(uintptr(h), uintptr(unsafe.Pointer(&mode)))
|
|
return mode, r != 0
|
|
}
|
|
|
|
func setConsoleMode(h syscall.Handle, mode uint32) {
|
|
_, _, _ = procSetConsoleMode.Call(uintptr(h), uintptr(mode))
|
|
}
|
|
|
|
// Remember the input mode at program start so we can restore it.
|
|
var (
|
|
savedInMode uint32
|
|
savedOutMode uint32
|
|
termSaved bool
|
|
)
|
|
|
|
func restoreCooked() {
|
|
hIn := syscall.Handle(os.Stdin.Fd())
|
|
hOut := syscall.Handle(os.Stdout.Fd())
|
|
if !termSaved {
|
|
if m, ok := getConsoleMode(hIn); ok {
|
|
savedInMode = m
|
|
}
|
|
if m, ok := getConsoleMode(hOut); ok {
|
|
savedOutMode = m
|
|
}
|
|
termSaved = true
|
|
}
|
|
// Cooked: line input + echo, plus VT so our ANSI rendering still works.
|
|
inMode := (savedInMode | enableProcessedInput | enableLineInput | enableEchoInput | enableVirtualTerminalInput)
|
|
setConsoleMode(hIn, inMode)
|
|
outMode := savedOutMode | enableProcessedOutput | enableVirtualTerminalProcessing
|
|
setConsoleMode(hOut, outMode)
|
|
}
|
|
|
|
func reenterRaw() {
|
|
hIn := syscall.Handle(os.Stdin.Fd())
|
|
hOut := syscall.Handle(os.Stdout.Fd())
|
|
// Raw: no line input, no echo, but keep VT so F-keys arrive as
|
|
// ESC[15~ etc. and our ANSI writes still render.
|
|
inMode := (savedInMode &^ (enableLineInput | enableEchoInput | enableProcessedInput)) | enableVirtualTerminalInput
|
|
setConsoleMode(hIn, inMode)
|
|
outMode := savedOutMode | enableProcessedOutput | enableVirtualTerminalProcessing
|
|
setConsoleMode(hOut, outMode)
|
|
}
|
|
|
|
func termSize() (int, int) {
|
|
hOut := syscall.Handle(os.Stdout.Fd())
|
|
var info consoleScreenBufferInfo
|
|
r, _, _ := procGetConsoleScreenBufferInfo.Call(uintptr(hOut), uintptr(unsafe.Pointer(&info)))
|
|
if r == 0 {
|
|
return 24, 80
|
|
}
|
|
cols := int(info.Window.Right - info.Window.Left + 1)
|
|
rows := int(info.Window.Bottom - info.Window.Top + 1)
|
|
return rows, cols
|
|
}
|
|
|
|
// readDebugKey reads a single key with raw console mode. VT input is
|
|
// enabled so F-keys / arrows arrive as the same ANSI escape sequences
|
|
// the Unix build expects, and decodeDebugKey classifies them.
|
|
func readDebugKey() int {
|
|
hIn := syscall.Handle(os.Stdin.Fd())
|
|
var before uint32
|
|
if m, ok := getConsoleMode(hIn); ok {
|
|
before = m
|
|
}
|
|
rawMode := (before &^ (enableLineInput | enableEchoInput | enableProcessedInput)) | enableVirtualTerminalInput
|
|
setConsoleMode(hIn, rawMode)
|
|
defer setConsoleMode(hIn, before)
|
|
|
|
buf := make([]byte, 8)
|
|
n, _ := os.Stdin.Read(buf)
|
|
return decodeDebugKey(buf, n)
|
|
}
|