// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Missing RTL functions needed by FiveSql2 and other Harbour programs. package hbrtl import ( "five/hbrdd" "five/hbrt" "os" "strings" ) // hb_FileExists(cFile) → lExists func HbFileExists(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProcFast() fname := t.Local(1).AsString() _, err := os.Stat(fname) t.PushBool(err == nil) t.RetValue() } // hb_Second(dTimestamp) → nSeconds (seconds portion of timestamp) func HbSecond(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProcFast() v := t.Local(1) if v.IsTimestamp() { ms := v.AsTimeMs() secs := int64(ms/1000) % 60 t.RetInt(secs) } else { t.RetInt(0) } } // hb_ATokens(cString [, cDelim]) → aTokens // Splits string by delimiter (default: space/tab/newline) func HbATokens(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() s := t.Local(1).AsString() delim := " " if nParams >= 2 && !t.Local(2).IsNil() { delim = t.Local(2).AsString() } var parts []string if delim == " " { parts = strings.Fields(s) } else { parts = strings.Split(s, delim) } items := make([]hbrt.Value, len(parts)) for i, p := range parts { items[i] = hbrt.MakeString(p) } t.PushValue(hbrt.MakeArrayFrom(items)) t.RetValue() } // hb_cdpSelect([cCodepage]) → cPrevCodepage // Stub: Five uses UTF-8 internally, codepage selection is a no-op. func HbCdpSelect(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() t.RetString("") } // Used() → lUsed — checks if current workarea is in use func Used(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProcFast() wam := getWA(t) if wam == nil { t.RetBool(false) return } t.RetBool(wam.Current() != nil) } // DBSETINDEX — SET INDEX TO (adds index to current workarea). // Previously a no-op; the generated code path for the SET INDEX TO // command bypasses this RTL, but SqlAttachTableIndexes (TSqlDDL.prg) // needs a runtime call so auto-attaching PK / UNIQUE indexes at // SqlExecOpenTable can happen without parser help. Missing file is // swallowed — matches Harbour's soft-fail semantics and keeps // pre-index tables silent. func rtlDbSetIndex(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() if nParams < 1 { t.RetNil() return } path := t.Local(1).AsString() if path == "" { t.RetNil() return } wam, ok := t.WA.(*hbrdd.WorkAreaManager) if !ok || wam == nil { t.RetNil() return } area := wam.Current() if area == nil { t.RetNil() return } // OrderListAdd lives on the optional Indexer interface — DBFNTX / // DBFCDX implement it, MEMRDD does not. Type-assert and silently // no-op on drivers without index support. if idx, ok := area.(hbrdd.Indexer); ok { _ = idx.OrderListAdd(path) } t.RetNil() }