// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com) // All rights reserved. // Console I/O functions for the Five runtime library. // Implements Harbour's QOut (?), QQOut (??), and related output functions. package hbrtl import ( "five/hbrt" "fmt" "os" "strings" ) // QOut implements the ? command. Prints newline then values separated by space. // Harbour: QOut() / hb_conOutStd() func QOut(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() args := make([]hbrt.Value, nParams) for i := 0; i < nParams; i++ { args[i] = t.Local(i + 1) } qoutImpl(args) t.RetNil() } // qoutImpl is the actual implementation called with pre-collected args. func qoutImpl(args []hbrt.Value) { parts := make([]string, len(args)) for i, v := range args { parts[i] = valueToDisplay(v) } fmt.Print("\r\n" + strings.Join(parts, " ")) } // qqoutImpl prints without leading newline (??). func qqoutImpl(args []hbrt.Value) { parts := make([]string, len(args)) for i, v := range args { parts[i] = valueToDisplay(v) } fmt.Print(strings.Join(parts, " ")) } // valueToDisplay converts a Value to its display string. // Harbour: hb_itemString() func valueToDisplay(v hbrt.Value) string { switch { case v.IsNil(): return "NIL" case v.IsLogical(): if v.AsBool() { return ".T." } return ".F." case v.IsInt(): return fmt.Sprintf("%d", v.AsInt()) case v.IsLong(): return fmt.Sprintf("%d", v.AsLong()) case v.IsDouble(): dec := v.Decimal() if dec == 255 { return fmt.Sprintf("%g", v.AsDouble()) } return fmt.Sprintf("%.*f", dec, v.AsDouble()) case v.IsString(): return v.AsString() case v.IsDate(): return julianToDateStr(v.AsJulian()) case v.IsTimestamp(): y, m, d := julianToDate(v.AsJulian()) ms := v.AsTimeMs() hh := ms / 3600000 mm := ms / 60000 % 60 ss := ms / 1000 % 60 return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, hh, mm, ss) case v.IsArray(): return fmt.Sprintf("{Array(%d)}", len(v.AsArray().Items)) default: return v.String() } } // rtlOutStd writes values to stdout without newline. Harbour: OutStd() func rtlOutStd(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() parts := make([]string, nParams) for i := 0; i < nParams; i++ { parts[i] = valueToDisplay(t.Local(i + 1)) } fmt.Print(strings.Join(parts, "")) t.RetNil() } // rtlOutErr writes values to stderr without newline. Harbour: OutErr() func rtlOutErr(t *hbrt.Thread) { nParams := t.ParamCount() t.Frame(nParams, 0) defer t.EndProcFast() parts := make([]string, nParams) for i := 0; i < nParams; i++ { parts[i] = valueToDisplay(t.Local(i + 1)) } fmt.Fprint(os.Stderr, strings.Join(parts, "")) t.RetNil() } // julianToDateStr and date formatting moved to datetime.go