// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Database callable functions: FIELDPUT, ALIAS, DBEVAL, DBUSEAREA, DBCLOSEAREA, // DBGOTO, DBSKIP, DBAPPEND, DBDELETE, DBRECALL, DBCOMMIT, DBSEEK, // DBGOTOP, DBGOBOTTOM, DBRLOCKLIST, DBSETFILTER, DBCLEARFILTER package hbrtl import ( "five/hbrt" "five/hbrdd" ) // FIELDPUT(nField, xValue) → xValue func rtlFieldPut(t *hbrt.Thread) { t.Frame(2, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area == nil { t.RetNil() return } nField := t.Local(1).AsInt() val := t.Local(2) area.PutValue(nField-1, val) // 1-based to 0-based t.RetVal(val) } // ALIAS([nWorkArea]) → cAlias func rtlAlias(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetString("") return } area := wam.Current() if area != nil { t.RetString(area.Alias()) } else { t.RetString("") } } // DBEVAL(bBlock [, bFor [, bWhile [, nCount [, nRecord [, lRest]]]]]) → NIL func rtlDbEval(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area == nil { t.RetNil() return } block := t.Local(1) if !block.IsBlock() { t.RetNil() return } var bFor, bWhile hbrt.Value nCount := -1 lRest := false if nParams >= 2 { bFor = t.Local(2) } if nParams >= 3 { bWhile = t.Local(3) } if nParams >= 4 && !t.Local(4).IsNil() { nCount = t.Local(4).AsInt() } if nParams >= 5 && !t.Local(5).IsNil() { nRec := t.Local(5).AsInt() area.GoTo(uint32(nRec)) } if nParams >= 6 && !t.Local(6).IsNil() { lRest = t.Local(6).AsBool() } // If not lRest and no record specified, go top if !lRest && (nParams < 5 || t.Local(5).IsNil()) { area.GoTop() } count := 0 for !area.EOF() { if nCount >= 0 && count >= nCount { break } // While condition if !bWhile.IsNil() && bWhile.IsBlock() { blk := bWhile.AsBlock() t.PendingParams2(0) blk.Fn(t) if !t.GetRetValue().AsBool() { break } } // For condition doBlock := true if !bFor.IsNil() && bFor.IsBlock() { blk := bFor.AsBlock() t.PendingParams2(0) blk.Fn(t) doBlock = t.GetRetValue().AsBool() } if doBlock { blk := block.AsBlock() t.PendingParams2(0) blk.Fn(t) } area.Skip(1) count++ } t.RetNil() } // DBUSEAREA([lNewArea], [cDriver], cName, [cAlias], [lShared], [lReadOnly]) → NIL func rtlDbUseArea(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } cName := "" cAlias := "" cDriver := "DBFNTX" if nParams >= 3 && !t.Local(3).IsNil() { cName = t.Local(3).AsString() } if nParams >= 4 && !t.Local(4).IsNil() { cAlias = t.Local(4).AsString() } if nParams >= 2 && !t.Local(2).IsNil() { cDriver = t.Local(2).AsString() } shared := false readOnly := false if nParams >= 5 && !t.Local(5).IsNil() { shared = t.Local(5).AsBool() } if nParams >= 6 && !t.Local(6).IsNil() { readOnly = t.Local(6).AsBool() } wam.Open(cDriver, cName, cAlias, shared, readOnly) t.RetNil() } // DBCLOSEAREA() → NIL func rtlDbCloseArea(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam != nil { wam.Close() } t.RetNil() } // DBGOTO(nRecNo) → NIL func rtlDbGoTo(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.GoTo(uint32(t.Local(1).AsLong())) } t.RetNil() } // DBSKIP([nRecords]) → NIL func rtlDbSkip(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area == nil { t.RetNil() return } n := int64(1) if nParams >= 1 && !t.Local(1).IsNil() { n = t.Local(1).AsLong() } area.Skip(n) t.RetNil() } // DBGOTOP() → NIL func rtlDbGoTop(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.GoTop() } t.RetNil() } // DBGOBOTTOM() → NIL func rtlDbGoBottom(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.GoBottom() } t.RetNil() } // DBAPPEND([lUnlock]) → NIL func rtlDbAppend(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Append() } t.RetNil() } // DBDELETE() → NIL func rtlDbDelete(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Delete() } t.RetNil() } // DBRECALL() → NIL func rtlDbRecall(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Recall() } t.RetNil() } // DBCOMMIT() → NIL func rtlDbCommit(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Flush() } t.RetNil() } // DBSEEK(xValue [, lSoftSeek [, lLast]]) → lFound func rtlDbSeek(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetBool(false) return } area := wam.Current() if area == nil { t.RetBool(false) return } val := t.Local(1) softSeek := false findLast := false if nParams >= 2 && !t.Local(2).IsNil() { softSeek = t.Local(2).AsBool() } if nParams >= 3 && !t.Local(3).IsNil() { findLast = t.Local(3).AsBool() } // Check if area implements Indexer if idx, ok := area.(hbrdd.Indexer); ok { found, _ := idx.Seek(val, softSeek, findLast) t.RetBool(found) } else { t.RetBool(false) } } // DBSELECTAREA(nArea | cAlias) → NIL func rtlDbSelectArea(t *hbrt.Thread) { t.Frame(1, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } v := t.Local(1) if v.IsString() { wam.Select(v.AsString()) } else { wam.Select(uint16(v.AsInt())) } t.RetNil() } // DBPACK() → NIL func rtlDbPack(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Pack() } t.RetNil() } // DBZAP() → NIL func rtlDbZap(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area != nil { area.Zap() } t.RetNil() } // --- LOCATE / CONTINUE --- // Harbour: DBLOCATE(bFor, bWhile, nNext, nRec, lRest) → .T./.F. // Searches from current position for record matching condition. func rtlDbLocate(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetBool(false) return } area := wam.Current() if area == nil { t.RetBool(false) return } // param 1: bFor condition block var bFor hbrt.Value if nParams >= 1 && !t.Local(1).IsNil() { bFor = t.Local(1) } // param 2: bWhile condition block var bWhile hbrt.Value if nParams >= 2 && !t.Local(2).IsNil() { bWhile = t.Local(2) } // param 3: nNext — max records to scan nNext := int64(0) // 0 = all if nParams >= 3 && !t.Local(3).IsNil() { nNext = t.Local(3).AsNumInt() } // param 4: nRec — specific record number if nParams >= 4 && !t.Local(4).IsNil() { nRec := uint32(t.Local(4).AsNumInt()) area.GoTo(nRec) if bFor.IsBlock() { t.PendingParams2(0) bFor.AsBlock().Fn(t) result := t.Pop2() area.SetFound(result.AsBool()) } else { area.SetFound(true) } t.RetBool(area.Found()) return } // param 5: lRest — .T. = continue from current, .F. = from top lRest := false if nParams >= 5 && !t.Local(5).IsNil() { lRest = t.Local(5).AsBool() } if !lRest && nNext == 0 { area.GoTop() } // Store locate block for __dbContinue if bFor.IsBlock() { area.SetLocate("", func(lt *hbrt.Thread) bool { lt.PendingParams2(0) bFor.AsBlock().Fn(lt) return lt.Pop2().AsBool() }) } found := false count := int64(0) for !area.EOF() { if nNext > 0 && count >= nNext { break } // Check WHILE condition if bWhile.IsBlock() { t.PendingParams2(0) bWhile.AsBlock().Fn(t) if !t.Pop2().AsBool() { break } } // Check FOR condition if bFor.IsBlock() { t.PendingParams2(0) bFor.AsBlock().Fn(t) if t.Pop2().AsBool() { found = true break } } else { found = true break } area.Skip(1) count++ } area.SetFound(found) t.RetBool(found) } // __DBCONTINUE — continue previous LOCATE search. // Harbour: __dbContinue() func rtlDbContinue(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetBool(false) return } area := wam.Current() if area == nil { t.RetBool(false) return } blk := area.LocateBlock() if blk == nil { area.SetFound(false) t.RetBool(false) return } // Skip past current record area.Skip(1) found := false for !area.EOF() { if blk(t) { found = true break } area.Skip(1) } area.SetFound(found) t.RetBool(found) } // --- DBSETFILTER / DBCLEARFILTER / DBFILTER --- // DBSETFILTER(bCondition [, cCondition]) func rtlDbSetFilter(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } area := wam.Current() if area == nil { t.RetNil() return } if nParams >= 1 && t.Local(1).IsBlock() { bFilter := t.Local(1) expr := "" if nParams >= 2 && t.Local(2).IsString() { expr = t.Local(2).AsString() } area.SetFilter(expr, func(lt *hbrt.Thread) bool { lt.PendingParams2(0) bFilter.AsBlock().Fn(lt) return lt.Pop2().AsBool() }) } t.RetNil() } // DBCLEARFILTER() func rtlDbClearFilter(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() wam := getWA(t) if wam == nil { t.RetNil() return } if area := wam.Current(); area != nil { area.ClearFilter() } t.RetNil() } // DBFILTER() → cFilterExpression func rtlDbFilter(t *hbrt.Thread) { t.Frame(0, 0) defer t.EndProc() // TODO: return stored filter expression from area t.RetString("") }