Py_sax2sab/config.py
arda.aydin@ontrol.com.tr dd7b586ea0 Drop testprogram/ from the repo, show version in dialog titles
testprogram/ is local test data, not part of the tool: untracked and
gitignored so it never comes back. verify.py now prints SKIP and exits 0
when the test pair is absent instead of blowing up.

Adds config.NAME / config.VERSION (sab2sax 0.0.0.001). Every dialog title
bar goes through config.title(), and the CLI prints the same line at
startup, so it is clear which build produced a file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:08:20 +03:00

189 lines
6.1 KiB
Python

"""Configuration: sedona home lives in `system.properties` next to this file.
Same key sedonac itself uses, but written as a plain Windows path:
sedona.home=C:\\path\\to\\sedona
We only *read* this file, so there is no reason to escape anything. Java's
Properties (what sedonac uses) treats `\\` as an escape, so its own files double
them up -- `\\\\path\\\\to`. Both parse here: `\\\\` collapses to `\\`, and every
other backslash is kept literally, so `C:\\niagara` does not turn into a newline.
If the key is missing, or the directory no longer holds `manifests/`, a folder
chooser is shown and the answer is written back to `system.properties`.
"""
import os, sys
NAME = "sab2sax"
VERSION = "0.0.0.001"
HERE = os.path.dirname(os.path.abspath(__file__))
PROPS = os.path.join(HERE, "system.properties")
KEY = "sedona.home"
def title(text):
"""Every dialog carries the program name and version in its title bar."""
return "%s %s - %s" % (NAME, VERSION, text)
# ---------- .properties ----------
def _unescape(v):
"""Collapse `\\\\` to `\\`; leave any other backslash alone.
Accepts both our plain paths and java-escaped ones, and never mistakes a
path segment for an escape (`C:\\niagara` stays `C:\\niagara`).
"""
out, i = [], 0
while i < len(v):
if v[i] == "\\" and i + 1 < len(v) and v[i + 1] == "\\":
i += 1
out.append(v[i])
i += 1
return "".join(out)
def read_props(path=PROPS):
props = {}
if not os.path.exists(path):
return props
with open(path, "r", encoding="utf-8", errors="replace") as f:
for line in f:
line = line.strip()
if not line or line[0] in "#!" or "=" not in line:
continue
k, v = line.split("=", 1)
props[k.strip()] = _unescape(v.strip()) # last wins
return props
def write_prop(key, value, path=PROPS):
"""Set `key`, keeping every other line of the file intact."""
lines = []
if os.path.exists(path):
with open(path, "r", encoding="utf-8", errors="replace") as f:
lines = f.read().splitlines()
new = "%s=%s" % (key, value)
done = False
for i, line in enumerate(lines):
s = line.strip()
if s and s[0] not in "#!" and "=" in s and s.split("=", 1)[0].strip() == key:
lines[i] = new if not done else "#" + line # comment out later dupes
done = True
if not done:
lines.append(new)
with open(path, "w", encoding="utf-8") as f:
f.write("\n".join(lines) + "\n")
# ---------- dialogs ----------
def _tk():
"""A hidden root window, or None when no GUI is available."""
try:
import tkinter as tk
root = tk.Tk()
root.withdraw()
return root
except Exception:
return None
def ask_directory(text, initial=None):
root = _tk()
if root is None:
return None
try:
from tkinter import filedialog
return filedialog.askdirectory(title=title(text), initialdir=initial or HERE) or None
finally:
root.destroy()
def ask_open_file(text, filetypes, initial=None):
root = _tk()
if root is None:
return None
try:
from tkinter import filedialog
return filedialog.askopenfilename(title=title(text), filetypes=filetypes,
initialdir=initial or HERE) or None
finally:
root.destroy()
def ask_save_file(text, filetypes, defaultextension, initialfile=None, initial=None):
root = _tk()
if root is None:
return None
try:
from tkinter import filedialog
return filedialog.asksaveasfilename(title=title(text), filetypes=filetypes,
defaultextension=defaultextension,
initialfile=initialfile,
initialdir=initial or HERE) or None
finally:
root.destroy()
# ---------- api ----------
def is_sedona_home(path):
return bool(path) and os.path.isdir(os.path.join(path, "manifests"))
def get_sedona_home(interactive=True):
"""Path from system.properties, prompting (and saving) when unusable."""
home = read_props().get(KEY)
if is_sedona_home(home):
return home
if not interactive:
raise SystemExit("%s not set (or has no manifests/) in %s" % (KEY, PROPS))
why = "not set in" if not home else "invalid (%s), update" % home
print("sedona.home %s %s -- please choose the sedona installation folder" % (why, PROPS))
while True:
picked = ask_directory("Select Sedona home (the folder containing manifests/)",
home if home and os.path.isdir(home) else None)
if not picked:
raise SystemExit("no sedona home selected")
picked = os.path.normpath(picked)
if is_sedona_home(picked):
write_prop(KEY, picked)
print("saved %s=%s to %s" % (KEY, picked, PROPS))
return picked
print(" %s has no manifests/ subfolder -- try again" % picked)
SAX = ("Sedona app XML", "*.sax")
SAB = ("Sedona app binary", "*.sab")
ANY = ("All files", "*.*")
def choose_input_file(initial=None):
"""Pick the file to convert -- direction follows its extension."""
return ask_open_file("Select the .sax or .sab application file to convert",
[("Sedona app (*.sax, *.sab)", "*.sax *.sab"), SAX, SAB, ANY],
initial)
def choose_sax_file(initial=None):
return ask_open_file("Select the .sax application file", [SAX, ANY], initial)
def choose_sab_file(initialfile=None, initial=None):
"""Save-as for the .sab produced from a .sax."""
return ask_save_file("Save the .sab application file", [SAB, ANY],
".sab", initialfile, initial)
def choose_sax_output(initialfile=None, initial=None):
"""Save-as for the .sax produced from a .sab."""
return ask_save_file("Save the .sax application file", [SAX, ANY],
".sax", initialfile, initial)
if __name__ == "__main__":
print(get_sedona_home())